"""
Sentiment Analysis Service
Uses transformer models for accurate sentiment classification
"""
import logging
from typing import List, Dict, Any, Optional
from transformers import AutoModelForSequenceClassification, AutoTokenizer
import torch
from textblob import TextBlob
import numpy as np

from app.core.config import settings

logger = logging.getLogger(__name__)


class SentimentService:
    """Advanced Sentiment Analysis using transformers"""

    def __init__(self):
        self.model = None
        self.tokenizer = None
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.labels = ["negative", "neutral", "positive"]

    async def load_models(self):
        """Load sentiment analysis models"""
        try:
            logger.info(f"Loading sentiment model: {settings.HF_MODEL_SENTIMENT}")
            self.tokenizer = AutoTokenizer.from_pretrained(settings.HF_MODEL_SENTIMENT)
            self.model = AutoModelForSequenceClassification.from_pretrained(
                settings.HF_MODEL_SENTIMENT
            )
            self.model.to(self.device)
            self.model.eval()
            logger.info("Sentiment model loaded successfully")
        except Exception as e:
            logger.warning(f"Failed to load transformer model, using fallback: {e}")
            self.model = None

    def analyze_text(self, text: str) -> Dict[str, Any]:
        """
        Analyze sentiment of a single text

        Args:
            text: Input text to analyze

        Returns:
            Sentiment analysis results
        """
        if not text or not text.strip():
            return {
                "sentiment": "neutral",
                "confidence": 0.0,
                "scores": {"negative": 0.33, "neutral": 0.34, "positive": 0.33}
            }

        # Try transformer model first
        if self.model is not None:
            try:
                return self._analyze_with_transformer(text)
            except Exception as e:
                logger.warning(f"Transformer analysis failed: {e}")

        # Fallback to TextBlob
        return self._analyze_with_textblob(text)

    def _analyze_with_transformer(self, text: str) -> Dict[str, Any]:
        """Use transformer model for sentiment analysis"""
        inputs = self.tokenizer(
            text,
            return_tensors="pt",
            truncation=True,
            max_length=512,
            padding=True
        )
        inputs = {k: v.to(self.device) for k, v in inputs.items()}

        with torch.no_grad():
            outputs = self.model(**inputs)
            scores = torch.nn.functional.softmax(outputs.logits, dim=-1)

        scores_np = scores.cpu().numpy()[0]
        predicted_class = int(np.argmax(scores_np))

        return {
            "sentiment": self.labels[predicted_class],
            "confidence": float(scores_np[predicted_class]),
            "scores": {
                label: float(score)
                for label, score in zip(self.labels, scores_np)
            }
        }

    def _analyze_with_textblob(self, text: str) -> Dict[str, Any]:
        """Fallback sentiment analysis using TextBlob"""
        blob = TextBlob(text)
        polarity = blob.sentiment.polarity

        if polarity > 0.1:
            sentiment = "positive"
            confidence = min(polarity + 0.5, 1.0)
        elif polarity < -0.1:
            sentiment = "negative"
            confidence = min(abs(polarity) + 0.5, 1.0)
        else:
            sentiment = "neutral"
            confidence = 0.6

        # Calculate approximate scores
        pos_score = (polarity + 1) / 2
        neg_score = 1 - pos_score
        neutral_score = 1 - abs(polarity)

        # Normalize
        total = pos_score + neg_score + neutral_score
        scores = {
            "positive": pos_score / total,
            "negative": neg_score / total,
            "neutral": neutral_score / total
        }

        return {
            "sentiment": sentiment,
            "confidence": confidence,
            "scores": scores,
            "polarity": polarity,
            "subjectivity": blob.sentiment.subjectivity
        }

    async def analyze_batch(self, texts: List[str]) -> List[Dict[str, Any]]:
        """
        Analyze sentiment of multiple texts

        Args:
            texts: List of texts to analyze

        Returns:
            List of sentiment analysis results
        """
        results = []
        for text in texts:
            result = self.analyze_text(text)
            results.append(result)
        return results

    async def analyze_conversation(
        self,
        messages: List[Dict[str, str]]
    ) -> Dict[str, Any]:
        """
        Analyze sentiment progression in a conversation

        Args:
            messages: List of message dicts with 'role' and 'content'

        Returns:
            Conversation sentiment analysis
        """
        user_messages = [m["content"] for m in messages if m.get("role") == "user"]

        if not user_messages:
            return {
                "overall_sentiment": "neutral",
                "sentiment_progression": [],
                "started_as": "neutral",
                "ended_as": "neutral",
                "trend": "stable"
            }

        # Analyze each message
        results = await self.analyze_batch(user_messages)

        # Extract sentiment progression
        sentiments = [r["sentiment"] for r in results]
        scores = [r["scores"]["positive"] - r["scores"]["negative"] for r in results]

        # Determine trend
        if len(scores) >= 2:
            first_half = np.mean(scores[:len(scores)//2])
            second_half = np.mean(scores[len(scores)//2:])
            diff = second_half - first_half

            if diff > 0.1:
                trend = "improving"
            elif diff < -0.1:
                trend = "declining"
            else:
                trend = "stable"
        else:
            trend = "stable"

        # Overall sentiment
        avg_score = np.mean(scores)
        if avg_score > 0.1:
            overall = "positive"
        elif avg_score < -0.1:
            overall = "negative"
        else:
            overall = "neutral"

        return {
            "overall_sentiment": overall,
            "average_score": float(avg_score),
            "sentiment_progression": results,
            "started_as": sentiments[0] if sentiments else "neutral",
            "ended_as": sentiments[-1] if sentiments else "neutral",
            "trend": trend,
            "message_count": len(user_messages)
        }

    def get_keywords_by_sentiment(
        self,
        texts: List[str],
        sentiments: List[str]
    ) -> Dict[str, List[str]]:
        """Extract keywords grouped by sentiment"""
        from collections import defaultdict

        keywords_by_sentiment = defaultdict(list)

        for text, sentiment in zip(texts, sentiments):
            blob = TextBlob(text)
            # Extract noun phrases as keywords
            for phrase in blob.noun_phrases:
                keywords_by_sentiment[sentiment].append(phrase.lower())

        # Get unique keywords
        return {
            sent: list(set(kws))[:20]  # Top 20 unique keywords
            for sent, kws in keywords_by_sentiment.items()
        }


# Singleton instance
sentiment_service = SentimentService()
