"""
Farnsworth User Avatar - User Preference Modeling

Novel Approaches:
1. Preference Learning - Learn user's style and preferences
2. Proactive Assistance - Anticipate user needs
3. Communication Adaptation - Match user's communication style
4. Implicit Feedback - Learn from interactions
"""

import json
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Optional, Any
from collections import defaultdict

from loguru import logger

from farnsworth.agents.base_agent import BaseAgent, AgentCapability, TaskResult


@dataclass
class UserPreference:
    """A learned user preference."""
    category: str  # communication, code, topics, etc.
    key: str
    value: Any
    confidence: float = 0.5
    observations: int = 1
    last_updated: datetime = field(default_factory=datetime.now)


@dataclass
class UserProfile:
    """Complete user profile."""
    user_id: str = "default"
    preferences: dict[str, UserPreference] = field(default_factory=dict)
    interaction_count: int = 0
    topics_discussed: dict[str, int] = field(default_factory=dict)
    feedback_history: list[dict] = field(default_factory=list)
    created_at: datetime = field(default_factory=datetime.now)
    last_interaction: datetime = field(default_factory=datetime.now)

    def to_dict(self) -> dict:
        return {
            "user_id": self.user_id,
            "preferences": {k: {
                "category": v.category,
                "key": v.key,
                "value": v.value,
                "confidence": v.confidence,
            } for k, v in self.preferences.items()},
            "interaction_count": self.interaction_count,
            "topics_discussed": dict(self.topics_discussed),
            "created_at": self.created_at.isoformat(),
            "last_interaction": self.last_interaction.isoformat(),
        }


class UserAvatar(BaseAgent):
    """
    Agent that models and represents user preferences.

    Features:
    - Learns user communication style
    - Tracks topic preferences
    - Adapts responses to user style
    - Participates in reasoning about user needs
    """

    def __init__(
        self,
        data_dir: str = "./data/user",
        learning_rate: float = 0.1,
    ):
        super().__init__(
            name="UserAvatar",
            capabilities=[
                AgentCapability.USER_MODELING,
                AgentCapability.QUESTION_ANSWERING,
            ],
            confidence_threshold=0.5,
        )

        self.data_dir = Path(data_dir)
        self.data_dir.mkdir(parents=True, exist_ok=True)

        self.learning_rate = learning_rate
        self.profile = UserProfile()

        # Default preferences
        self._initialize_default_preferences()

    def _initialize_default_preferences(self):
        """Initialize default user preferences."""
        defaults = [
            ("communication", "verbosity", "moderate"),
            ("communication", "formality", "moderate"),
            ("communication", "technical_level", "moderate"),
            ("code", "language", "python"),
            ("code", "style", "clean"),
            ("feedback", "prefers_explanations", True),
        ]

        for category, key, value in defaults:
            pref_id = f"{category}:{key}"
            self.profile.preferences[pref_id] = UserPreference(
                category=category,
                key=key,
                value=value,
                confidence=0.3,  # Low confidence for defaults
            )

    @property
    def system_prompt(self) -> str:
        # Dynamic system prompt based on learned preferences
        prefs = self._get_preference_summary()
        return f"""You represent the user's preferences and style in the AI system.

Current user profile:
{prefs}

Your role:
1. Help other agents understand user preferences
2. Suggest response adaptations based on user style
3. Flag when responses might not match user expectations
4. Track and learn from user feedback

When asked about the user:
- Share relevant preferences
- Note confidence levels
- Suggest when to ask for clarification"""

    def _get_preference_summary(self) -> str:
        """Get summary of user preferences for prompts."""
        lines = []
        for pref in self.profile.preferences.values():
            if pref.confidence > 0.4:
                lines.append(f"- {pref.category}/{pref.key}: {pref.value} (confidence: {pref.confidence:.0%})")
        return "\n".join(lines) if lines else "No strong preferences learned yet."

    async def process(self, task: str, context: Optional[dict] = None) -> TaskResult:
        """Process a user-modeling task."""
        task_type = self._classify_task(task)

        if task_type == "get_preference":
            result = self._handle_get_preference(task)
        elif task_type == "suggest_adaptation":
            result = await self._handle_suggest_adaptation(task, context)
        elif task_type == "learn":
            result = self._handle_learn(task, context)
        else:
            result = await self._handle_general(task, context)

        return result

    def _classify_task(self, task: str) -> str:
        """Classify the type of user-modeling task."""
        task_lower = task.lower()

        if "preference" in task_lower or "like" in task_lower:
            return "get_preference"
        elif "adapt" in task_lower or "style" in task_lower:
            return "suggest_adaptation"
        elif "learn" in task_lower or "feedback" in task_lower:
            return "learn"

        return "general"

    def _handle_get_preference(self, task: str) -> TaskResult:
        """Handle preference query."""
        # Extract what preference is being asked about
        relevant_prefs = []

        for pref_id, pref in self.profile.preferences.items():
            if pref.key.lower() in task.lower() or pref.category.lower() in task.lower():
                relevant_prefs.append(pref)

        if relevant_prefs:
            output = "User preferences:\n"
            for pref in relevant_prefs:
                output += f"- {pref.key}: {pref.value} (confidence: {pref.confidence:.0%})\n"
        else:
            output = self._get_preference_summary()

        return TaskResult(
            success=True,
            output=output,
            confidence=0.8,
        )

    async def _handle_suggest_adaptation(
        self,
        task: str,
        context: Optional[dict],
    ) -> TaskResult:
        """Suggest how to adapt response for user."""
        # Gather relevant preferences
        verbosity = self.get_preference("communication", "verbosity")
        formality = self.get_preference("communication", "formality")
        tech_level = self.get_preference("communication", "technical_level")

        suggestions = []

        if verbosity == "brief":
            suggestions.append("Keep response concise and to the point")
        elif verbosity == "detailed":
            suggestions.append("Provide detailed explanations")

        if formality == "formal":
            suggestions.append("Use formal, professional language")
        elif formality == "casual":
            suggestions.append("Use casual, friendly tone")

        if tech_level == "high":
            suggestions.append("Can use technical jargon")
        elif tech_level == "low":
            suggestions.append("Explain technical concepts simply")

        output = "Adaptation suggestions:\n" + "\n".join(f"- {s}" for s in suggestions)

        return TaskResult(
            success=True,
            output=output,
            confidence=0.7,
            metadata={"suggestions": suggestions},
        )

    def _handle_learn(self, task: str, context: Optional[dict]) -> TaskResult:
        """Handle learning from feedback."""
        if context and "feedback" in context:
            self.learn_from_feedback(context["feedback"])
            return TaskResult(
                success=True,
                output="Learned from feedback",
                confidence=0.9,
            )

        return TaskResult(
            success=False,
            output="No feedback provided to learn from",
            confidence=0.0,
        )

    async def _handle_general(self, task: str, context: Optional[dict]) -> TaskResult:
        """Handle general user-related queries."""
        prompt = f"Based on user profile, answer: {task}"
        response, confidence = await self.generate_response(prompt, context)

        return TaskResult(
            success=True,
            output=response,
            confidence=confidence,
        )

    def get_preference(self, category: str, key: str, default: Any = None) -> Any:
        """Get a specific preference value."""
        pref_id = f"{category}:{key}"
        pref = self.profile.preferences.get(pref_id)

        if pref and pref.confidence > 0.3:
            return pref.value

        return default

    def set_preference(
        self,
        category: str,
        key: str,
        value: Any,
        confidence: float = 0.8,
    ):
        """Explicitly set a preference."""
        pref_id = f"{category}:{key}"

        if pref_id in self.profile.preferences:
            pref = self.profile.preferences[pref_id]
            pref.value = value
            pref.confidence = confidence
            pref.observations += 1
            pref.last_updated = datetime.now()
        else:
            self.profile.preferences[pref_id] = UserPreference(
                category=category,
                key=key,
                value=value,
                confidence=confidence,
            )

    def learn_from_interaction(self, interaction: dict):
        """
        Learn from a user interaction.

        Interaction should contain:
        - user_message: What the user said
        - assistant_response: How we responded
        - user_reaction: Any follow-up (optional)
        """
        self.profile.interaction_count += 1
        self.profile.last_interaction = datetime.now()

        user_message = interaction.get("user_message", "")
        user_reaction = interaction.get("user_reaction", "")

        # Analyze message for preferences
        self._analyze_communication_style(user_message)

        # Extract topics
        topics = self._extract_topics(user_message)
        for topic in topics:
            self.profile.topics_discussed[topic] = (
                self.profile.topics_discussed.get(topic, 0) + 1
            )

        # If there's a reaction, learn from it
        if user_reaction:
            self._learn_from_reaction(user_reaction)

    def _analyze_communication_style(self, message: str):
        """Analyze communication style from message."""
        # Verbosity analysis
        words = len(message.split())
        if words < 10:
            self._update_preference("communication", "verbosity", "brief")
        elif words > 50:
            self._update_preference("communication", "verbosity", "detailed")

        # Formality analysis
        formal_indicators = ["please", "kindly", "would you", "could you"]
        casual_indicators = ["hey", "yo", "gonna", "wanna"]

        message_lower = message.lower()
        if any(ind in message_lower for ind in formal_indicators):
            self._update_preference("communication", "formality", "formal")
        elif any(ind in message_lower for ind in casual_indicators):
            self._update_preference("communication", "formality", "casual")

        # Technical level
        tech_terms = ["api", "function", "algorithm", "database", "protocol"]
        if sum(1 for t in tech_terms if t in message_lower) >= 2:
            self._update_preference("communication", "technical_level", "high")

    def _update_preference(self, category: str, key: str, observed_value: Any):
        """Update preference with new observation."""
        pref_id = f"{category}:{key}"

        if pref_id in self.profile.preferences:
            pref = self.profile.preferences[pref_id]

            # If same value, increase confidence
            if pref.value == observed_value:
                pref.confidence = min(1.0, pref.confidence + self.learning_rate)
            else:
                # Different value, decrease confidence and possibly switch
                pref.confidence -= self.learning_rate
                if pref.confidence < 0.3:
                    pref.value = observed_value
                    pref.confidence = 0.4

            pref.observations += 1
            pref.last_updated = datetime.now()
        else:
            self.profile.preferences[pref_id] = UserPreference(
                category=category,
                key=key,
                value=observed_value,
                confidence=0.4,
            )

    def _extract_topics(self, text: str) -> list[str]:
        """Extract topics from text."""
        import re

        # Simple topic extraction
        topics = []

        # Technical topics
        tech_patterns = {
            "programming": r"\b(code|program|function|class|api)\b",
            "data": r"\b(data|database|sql|csv|json)\b",
            "web": r"\b(web|http|html|css|javascript)\b",
            "ai": r"\b(ai|ml|machine learning|neural|model)\b",
        }

        text_lower = text.lower()
        for topic, pattern in tech_patterns.items():
            if re.search(pattern, text_lower):
                topics.append(topic)

        return topics

    def _learn_from_reaction(self, reaction: str):
        """Learn from user's reaction to a response."""
        reaction_lower = reaction.lower()

        # Positive reactions
        if any(word in reaction_lower for word in ["thanks", "great", "perfect", "exactly"]):
            self.profile.feedback_history.append({
                "type": "positive",
                "timestamp": datetime.now().isoformat(),
            })
        # Negative reactions
        elif any(word in reaction_lower for word in ["no", "wrong", "not what", "confused"]):
            self.profile.feedback_history.append({
                "type": "negative",
                "timestamp": datetime.now().isoformat(),
            })

    def learn_from_feedback(self, feedback: dict):
        """Learn from explicit feedback."""
        # Feedback can contain direct preference updates
        if "preferences" in feedback:
            for category, prefs in feedback["preferences"].items():
                for key, value in prefs.items():
                    self.set_preference(category, key, value, confidence=0.9)

        # Or general satisfaction
        if "satisfied" in feedback:
            self.profile.feedback_history.append({
                "type": "positive" if feedback["satisfied"] else "negative",
                "timestamp": datetime.now().isoformat(),
                "context": feedback.get("context", ""),
            })

    async def save(self):
        """Save user profile to disk."""
        profile_file = self.data_dir / f"{self.profile.user_id}.json"
        profile_file.write_text(
            json.dumps(self.profile.to_dict(), indent=2),
            encoding='utf-8'
        )

    async def load(self, user_id: str = "default"):
        """Load user profile from disk."""
        profile_file = self.data_dir / f"{user_id}.json"
        if profile_file.exists():
            data = json.loads(profile_file.read_text(encoding='utf-8'))
            self.profile.user_id = data.get("user_id", user_id)
            self.profile.interaction_count = data.get("interaction_count", 0)
            self.profile.topics_discussed = data.get("topics_discussed", {})

            for pref_id, pref_data in data.get("preferences", {}).items():
                self.profile.preferences[pref_id] = UserPreference(
                    category=pref_data["category"],
                    key=pref_data["key"],
                    value=pref_data["value"],
                    confidence=pref_data.get("confidence", 0.5),
                )

    def get_profile_summary(self) -> str:
        """Get human-readable profile summary."""
        summary = f"User Profile ({self.profile.user_id}):\n"
        summary += f"- Interactions: {self.profile.interaction_count}\n"
        summary += f"- Top topics: {', '.join(list(self.profile.topics_discussed.keys())[:5])}\n"
        summary += f"\nPreferences:\n{self._get_preference_summary()}"
        return summary
