"""
Conversation for Alya Bot.
"""
import logging
import random
from typing import Dict, List, Optional, Any
import asyncio

from telegram import Update
from telegram.constants import ChatAction
from telegram.ext import ContextTypes, MessageHandler, filters

from config.settings import (
    COMMAND_PREFIX,
    FEATURES,
    ADMIN_IDS,
    AFFECTION_POINTS,
    RELATIONSHIP_LEVELS,
    RELATIONSHIP_THRESHOLDS,
    DEFAULT_LANGUAGE
)
from core.gemini_client import GeminiClient
from core.persona import PersonaManager
from core.memory import MemoryManager
from database.database_manager import db_manager, get_user_lang
from core.nlp import NLPEngine, ContextManager
from utils.formatters import format_response, format_error_response, format_paragraphs

logger = logging.getLogger(__name__)

class ConversationHandler:
    """Handler for conversation functionality with Alya."""
    
    def __init__(
        self,
        gemini_client: GeminiClient,
        persona_manager: PersonaManager, 
        memory_manager: MemoryManager,
        nlp_engine: Optional[NLPEngine] = None
    ) -> None:
        self.gemini = gemini_client
        self.persona = persona_manager
        self.memory = memory_manager
        self.db = db_manager
        self.context_manager = ContextManager(self.db)  # <-- DB-backed context manager
        self.nlp = nlp_engine or NLPEngine()
    
    def get_handlers(self) -> List:
        handlers = [
            MessageHandler(
                filters.TEXT & filters.ChatType.PRIVATE & ~filters.COMMAND,
                self.chat_command
            ),
            MessageHandler(
                (
                    filters.TEXT & filters.ChatType.GROUPS & ~filters.COMMAND &
                    (
                        filters.Regex(f"^{COMMAND_PREFIX}") |
                        filters.REPLY
                    )
                ),
                self.chat_command
            ),
        ]
        return handlers
    
    def _create_or_update_user(self, user) -> bool:
        is_admin = user.id in ADMIN_IDS or self.db.is_admin(user.id)
        self.db.get_or_create_user(
            user.id, 
            username=user.username or "", 
            first_name=user.first_name or "", 
            last_name=user.last_name or "",
            is_admin=is_admin
        )
        return is_admin
    
    def _get_relationship_level(self, user_id: int) -> int:
        user_info = self.db.get_user_relationship_info(user_id)
        return user_info.get("relationship", {}).get("level", 0) if user_info else 0
    
    async def _send_error_response(self, update: Update, username: str, lang: str) -> None:
        error_message = self.persona.get_error_message(username=username or "user", lang=lang)
        formatted_error = format_error_response(error_message)
        await update.message.reply_html(formatted_error)
            
    async def chat_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        user = update.effective_user
        message_text = update.message.text
        lang = get_user_lang(user.id) # Get user language

        reply_context = ""
        is_reply_to_alya = False
        replied_message_is_conversation = False
        if update.message.reply_to_message:
            replied = update.message.reply_to_message
            if replied.from_user and replied.from_user.is_bot:
                if replied.from_user.id == context.bot.id:
                    reply_context = replied.text or ""
                    is_reply_to_alya = True
                    if replied.text and replied.text.endswith("\u200C"):
                        replied_message_is_conversation = True

        if update.message.chat.type in ["group", "supergroup"]:
            if is_reply_to_alya:
                if update.message.reply_to_message and not replied_message_is_conversation:
                    return
                query = message_text.strip()
            else:
                if message_text.startswith(COMMAND_PREFIX):
                    query = message_text.replace(COMMAND_PREFIX, "", 1).strip()
                else:
                    return
        else:
            query = message_text.strip()

        bot_username = (await context.bot.get_me()).username if hasattr(context.bot, "get_me") else None
        if query.startswith("/"):
            if bot_username:
                if query.split()[0].lower().startswith(f"/") and f"@{bot_username.lower()}" in query.split()[0].lower():
                    return
            else:
                return
            return

        if reply_context:
            query = f"{reply_context}\n\n{query}"
        if not query:
            help_message = self.persona.get_help_message(
                username=user.first_name or "user",
                prefix=COMMAND_PREFIX,
                lang=lang  # Pass language parameter
            )
            formatted_help = format_response(help_message, "neutral")
            await update.message.reply_html(formatted_help)
            return
        chat = update.effective_chat
        try:
            if hasattr(update.message, "message_thread_id") and update.message.message_thread_id:
                await context.bot.send_chat_action(
                    chat_id=chat.id,
                    action=ChatAction.TYPING,
                    message_thread_id=update.message.message_thread_id
                )
            else:
                await context.bot.send_chat_action(
                    chat_id=chat.id,
                    action=ChatAction.TYPING
                )
        except Exception as e:
            logger.warning(f"Failed to send typing action: {e}")
        try:
            self._create_or_update_user(user)
            self.db.save_message(user.id, "user", query)
            self.memory.save_user_message(user.id, query)
            self.context_manager.apply_sliding_window(user.id)
            user_context = await self._prepare_conversation_context(user, query, lang)
            history = self.context_manager.get_context_window(user.id)
            response = await self.gemini.generate_response(
                user_id=user.id,
                username=user.first_name or "user",
                message=user_context["enhanced_query"],
                context=user_context["system_prompt"],
                relationship_level=user_context["relationship_level"],
                is_admin=user.id in ADMIN_IDS or self.db.is_admin(user.id),
                lang=lang,
                retry_count=3,
                is_media_analysis=False,
                media_context=None
            )
            if response:
                await self._process_and_send_response(update, user, response, user_context["message_context"], lang)
            else:
                await self._send_error_response(update, user.first_name, lang)
        except Exception as e:
            logger.error(f"Error in chat command: {e}", exc_info=True)
            await self._send_error_response(update, user.first_name, lang)

    async def _send_chat_action(
        self, update: Update, context: ContextTypes.DEFAULT_TYPE, action: str
    ) -> None:
        try:
            if hasattr(update.message, "message_thread_id") and update.message.message_thread_id:
                await context.bot.send_chat_action(
                    chat_id=update.effective_chat.id,
                    action=action,
                    message_thread_id=update.message.message_thread_id
                )
            else:
                await context.bot.send_chat_action(
                    chat_id=update.effective_chat.id,
                    action=action
                )
        except Exception as e:
            logger.warning(f"Failed to send chat action: {e}")
    
    async def _prepare_conversation_context(self, user, query: str, lang: str) -> Dict[str, Any]:
        user_task = asyncio.create_task(self._get_user_info(user))
        self.memory.save_user_message(user.id, query)
        relationship_level = self._get_relationship_level(user.id)
        # Gabungkan persona prompt ke context
        persona_prompt = self.persona.get_chat_prompt(
            username=user.first_name,
            message=query,
            context="\n".join([str(c) for c in self.context_manager.get_context_window(user.id)]) if self.context_manager.get_context_window(user.id) else "",
            relationship_level=relationship_level,
            is_admin=user.id in ADMIN_IDS or self.db.is_admin(user.id),
            lang=lang
        )
        # Improved context extraction
        message_context = {}
        semantic_topics = []
        if FEATURES.get("emotion_detection", False) and self.nlp:
            message_context = self.nlp.get_message_context(query, user.id)
            semantic_topics = message_context.get("semantic_topics", [])
            flow_analysis = self.nlp.analyze_conversation_flow(user.id, query)
            message_context["conversation_flow"] = flow_analysis
            if flow_analysis.get("is_continuation", False):
                persona_prompt += "\n\nCONVERSATION CONTEXT: This seems to be a continuation of our previous topic. Please maintain context continuity and reference our previous discussion naturally."
            if flow_analysis.get("user_engagement_level") == "high":
                persona_prompt += "\n\nUSER ENGAGEMENT: The user seems very engaged and interested. Match their energy level and be more expressive in your response."
            elif flow_analysis.get("user_engagement_level") == "low":
                persona_prompt += "\n\nUSER ENGAGEMENT: The user seems less engaged. Try to be more encouraging and ask questions to increase engagement."
        history = self.context_manager.get_context_window(user.id)
        prev_messages = self.db.get_conversation_history(user.id, limit=5)
        prev_content = "\n".join([msg.get("content", "") for msg in prev_messages if msg.get("role") == "user"])
        summaries = self.context_manager.get_conversation_summaries(user.id)
        conversation_summary = summaries[0].get('content', '') if summaries else "No previous context"
        enhanced_query = self._call_method_safely(self.memory.create_context_prompt, user.id, query, lang)
        conversation_context = {
            "current_topic": ", ".join(semantic_topics) if semantic_topics else "general conversation",
            "user_emotion": message_context.get("emotion", "neutral"),
            "conversation_history_summary": conversation_summary,
            "previous_user_messages": prev_content
        }
        relationship_context = self._get_relationship_context(user, relationship_level, user.id in ADMIN_IDS, lang)
        conversation_theme = self._get_conversation_theme_context(conversation_context)
        if relationship_context:
            persona_prompt += f"\n\n{relationship_context}"
        if conversation_theme:
            persona_prompt += f"\n\n{conversation_theme}"
        await user_task
        return {
            "history": history,
            "enhanced_query": enhanced_query,
            "system_prompt": persona_prompt,
            "message_context": message_context,
            "relationship_level": relationship_level,
            "conversation_context": conversation_context
        }

    def _get_conversation_theme_context(self, conversation_context: Dict[str, Any]) -> str:
        """Generate conversation theme context for more natural responses.
        
        Args:
            conversation_context: Current conversation context
        
        Returns:
            Conversation theme context string
        """
        topic = conversation_context.get("current_topic", "general conversation")
        emotion = conversation_context.get("user_emotion", "neutral")
        
        # Create more natural, context-aware prompt
        context = f"""
CONTEXT AWARENESS:
- Current topic appears to be about: {topic}
- User's detected emotion: {emotion}
- Recent conversation history: {conversation_context.get("conversation_history_summary", "No recent history")}

Based on this context:
1. Respond naturally to the topic at hand
2. Show appropriate emotional awareness and empathy
3. Reference previous parts of the conversation when relevant
4. Use roleplay that fits the mood and topic
5. Show understanding of the conversational flow
"""
        return context
    
    def _call_method_safely(self, method, *args, **kwargs):
        if asyncio.iscoroutinefunction(method):
            return asyncio.create_task(method(*args, **kwargs))
        else:
            return method(*args, **kwargs)
        
    async def _ensure_language(self, text: str, lang: str, user) -> str:
        """Ensure text is in the user's preferred language using LLM translation if needed."""
        from utils.formatters import get_translate_prompt
        import langdetect
        preferred_lang = lang or DEFAULT_LANGUAGE
        try:
            detected_lang = langdetect.detect(text)
        except Exception:
            detected_lang = preferred_lang
        if detected_lang != preferred_lang:
            translate_prompt = get_translate_prompt(text, preferred_lang)
            try:
                translated = await self.gemini.generate_response(
                    user_id=user.id,
                    username=user.first_name or "user",
                    message=translate_prompt,
                    context="",
                    relationship_level=1,
                    is_admin=False,
                    lang=preferred_lang,
                    retry_count=2,
                    is_media_analysis=False,
                    media_context=None
                )
                if translated and isinstance(translated, str):
                    return translated.strip()
            except Exception as e:
                logger.error(f"Translation step failed: {e}")
        return text

    async def _process_and_send_response(
        self,
        update: Update,
        user,
        response: str,
        message_context: Dict[str, Any],
        lang: str
    ) -> None:
        """Process Alya's response: ensure language, format, and send to Telegram."""
        self.db.save_message(user.id, "assistant", response)
        self.memory.save_bot_response(user.id, response)
        if message_context:
            self._update_affection_from_context(user.id, message_context)

        # --- Detect emotion, intent, topic ---
        emotion = message_context.get("emotion", "neutral") if message_context else "neutral"
        intent = message_context.get("intent", "") if message_context else ""
        topic = message_context.get("topic", "any") if message_context else "any"

        # --- Ensure response in user language (before formatting) ---
        response = await self._ensure_language(response, lang, user)

        # --- Map emotion to mood/intensity ---
        emotion_mood_mapping = {
            "happy": ("excited", 0.8),
            "excited": ("excited", 0.9),
            "grateful": ("comfortable_tsundere", 0.6),
            "sad": ("melancholic", 0.4),
            "angry": ("defensive", 0.8),
            "worried": ("nervous", 0.5),
            "embarrassed": ("embarrassed", 0.7),
            "surprised": ("surprised", 0.8),
            "neutral": ("default", 0.5)
        }
        mood, intensity = emotion_mood_mapping.get(emotion, ("default", 0.5))

        # --- Persona roleplay/action mapping ---
        roleplay_mapping = self.persona.get_roleplay_mapping(
            emotion=emotion,
            intent=intent,
            topic=topic,
            mood=mood,
            lang=lang
        )
        roleplay_action = roleplay_mapping.get("roleplay_action", "")
        russian_expression = roleplay_mapping.get("russian_expression", "")

        # --- Format response ---
        formatted_response = format_response(
            response,
            emotion=emotion,
            mood=mood,
            intensity=intensity,
            username=user.first_name or "user",
            roleplay_action=roleplay_action,
            russian_expression=russian_expression
        )
        formatted_response = format_paragraphs(formatted_response, markdown=False)
        formatted_response = f"{formatted_response}\u200C"

        # --- Ensure ALL output in user language (after formatting) ---
        formatted_response = await self._ensure_language(formatted_response, lang, user)

        await update.message.reply_html(formatted_response)
    
    async def _get_user_info(self, user) -> Dict[str, Any]:
        is_admin = user.id in ADMIN_IDS or (self.db and self.db.is_admin(user.id))
        if self.db:
            self._create_or_update_user(user)
            relationship_level = self._get_relationship_level(user.id)
        else:
            relationship_level = 0
        return {
            'is_admin': is_admin,
            'relationship_level': relationship_level
        }

    def _get_relationship_context(self, user: Any, relationship_level: int, is_admin: bool, lang: str = "id") -> str:
        """Get relationship context in the specified language."""
        first_name = getattr(user, 'first_name', None) or "user"
        
        # Get relationship context from persona manager instead of hardcoding
        return self.persona.get_relationship_context(
            username=first_name,
            relationship_level=relationship_level,
            is_admin=is_admin,
            lang=lang
        )
    
    def _try_level_up(self, user_id: int) -> None:
        """
        Attempt to level up user relationship if eligible.
        """
        try:
            from database.session import db_session_context
            from database.models import User
            
            with db_session_context() as session:
                user = session.query(User).filter(User.id == user_id).first()
                if not user:
                    return
                
                current_level = user.relationship_level
                next_level = current_level + 1
                
                if next_level not in RELATIONSHIP_LEVELS:
                    return  # Already at max level

                interaction_count = user.interaction_count
                affection_points = user.affection_points
                
                interaction_threshold = RELATIONSHIP_THRESHOLDS["interaction_count"].get(next_level, float('inf'))
                affection_threshold = RELATIONSHIP_THRESHOLDS["affection_points"].get(next_level, float('inf'))

                if interaction_count >= interaction_threshold and affection_points >= affection_threshold:
                    self.db.update_relationship_level(user_id, next_level)
                    logger.info(f"User {user_id} leveled up to level {next_level} ({RELATIONSHIP_LEVELS[next_level]})")
                    
        except Exception as e:
            logger.error(f"Error in _try_level_up for user {user_id}: {e}")

    def _update_affection_from_context(self, user_id: int, message_context: Dict[str, Any]) -> None:
        """
        Update Alya's affection points towards user based on how user treats Alya.
        
        Logic: 
        - User treats Alya nicely → Alya's affection increases
        - User treats Alya badly → Alya's affection decreases
        
        Args:
            user_id: The user's telegram ID
            message_context: NLP analysis results from user's message
        """
        if not message_context:
            # Base affection for normal conversation (if no analysis available)
            self.db.update_affection(user_id, AFFECTION_POINTS.get("conversation", 1))
            self._try_level_up(user_id)
            return

        affection_delta = 0

        # === USER'S POSITIVE EMOTIONS/BEHAVIORS TOWARDS ALYA ===
        # When user shows positive emotions, Alya feels appreciated
        emotion = message_context.get("emotion", "")
        if emotion in ["happy", "excited", "grateful", "joy", "love", "admiration"]:
            affection_delta += AFFECTION_POINTS.get("positive_emotion", 2)
            logger.debug(f"User {user_id} shows positive emotion '{emotion}': +{AFFECTION_POINTS.get('positive_emotion', 2)}")
        elif emotion in ["sad", "worried", "disappointed"]:
            # User sharing vulnerable emotions with Alya shows trust
            affection_delta += AFFECTION_POINTS.get("mild_positive_emotion", 1)
            logger.debug(f"User {user_id} shows vulnerable emotion '{emotion}': +{AFFECTION_POINTS.get('mild_positive_emotion', 1)}")
        elif emotion in ["angry", "frustrated", "annoyed"] and not message_context.get("directed_at_alya", False):
            # User is angry but not at Alya (venting to Alya as friend)
            affection_delta += AFFECTION_POINTS.get("mild_positive_emotion", 1)
        elif emotion in ["angry", "frustrated", "annoyed"] and message_context.get("directed_at_alya", True):
            # User is angry AT Alya
            affection_delta += AFFECTION_POINTS.get("anger", -3)
            logger.debug(f"User {user_id} is angry at Alya: {AFFECTION_POINTS.get('anger', -3)}")

        # === USER'S INTENTIONS TOWARDS ALYA ===
        intent = message_context.get("intent", "")
        if intent == "gratitude":
            affection_delta += AFFECTION_POINTS.get("gratitude", 5)
        elif intent == "apology":
            affection_delta += AFFECTION_POINTS.get("apology", 2)
        elif intent == "affection":  # User expressing love/care to Alya
            affection_delta += AFFECTION_POINTS.get("affection", 5)
        elif intent == "greeting":
            affection_delta += AFFECTION_POINTS.get("greeting", 2)
        elif intent == "compliment":  # User complimenting Alya
            affection_delta += AFFECTION_POINTS.get("compliment", 10)
        elif intent == "question":
            affection_delta += AFFECTION_POINTS.get("question", 1)
        elif intent == "meaningful_conversation":
            affection_delta += AFFECTION_POINTS.get("meaningful_conversation", 8)
        elif intent == "asking_about_alya":  # User showing interest in Alya
            affection_delta += AFFECTION_POINTS.get("asking_about_alya", 7)
        elif intent == "remembering_details":  # User remembering things about Alya
            affection_delta += AFFECTION_POINTS.get("remembering_details", 15)
        elif intent in ["insult", "abuse"]:  # User insulting Alya
            affection_delta += AFFECTION_POINTS.get("insult", -10)
        elif intent in ["toxic", "toxic_behavior", "bullying"]:  # User being toxic to Alya
            affection_delta += AFFECTION_POINTS.get("toxic_behavior", -10)
        elif intent == "rudeness":  # User being rude to Alya
            affection_delta += AFFECTION_POINTS.get("rudeness", -10)
        elif intent == "ignoring":  # User ignoring Alya
            affection_delta += AFFECTION_POINTS.get("ignoring", -5)
        elif intent == "inappropriate":  # User being inappropriate to Alya
            affection_delta += AFFECTION_POINTS.get("inappropriate", -20)
        elif intent in ["command", "departure"]:
            affection_delta += AFFECTION_POINTS.get("command", -1)

        # === RELATIONSHIP SIGNALS ===
        relationship_signals = message_context.get("relationship_signals", {})
        affection_delta += relationship_signals.get("friendliness", 0) * AFFECTION_POINTS.get("friendliness", 6)
        affection_delta += relationship_signals.get("romantic_interest", 0) * AFFECTION_POINTS.get("romantic_interest", 10)
        affection_delta += relationship_signals.get("conflict", 0) * AFFECTION_POINTS.get("conflict", -3)

        # Apply minimum penalty limit
        min_penalty = AFFECTION_POINTS.get("min_penalty", -4)
        if affection_delta < 0:
            affection_delta = max(affection_delta, min_penalty)

        # Base affection for normal conversation (if no significant change detected)
        if affection_delta == 0:
            affection_delta = AFFECTION_POINTS.get("conversation", 1)

        # Apply affection change if significant enough
        if abs(affection_delta) >= 1:
            affection_delta = round(affection_delta)
            logger.debug(f"Updating affection for user {user_id}: {affection_delta:+d} points")
            self.db.update_affection(user_id, affection_delta)
            self._try_level_up(user_id)