"""
Tests for Sentiment Service

This module tests sentiment service functionality including database operations.
"""

import pytest
from sqlalchemy.orm import Session
from datetime import datetime

from app.services.sentiment_service import (
    process_tweet_sentiment,
    process_tweet_batch,
    process_reddit_post_sentiment,
    process_reddit_post_batch,
    get_sentiment_by_entity,
    get_sentiments_by_match,
    calculate_match_sentiment_metrics,
    get_global_sentiment_metrics
)
from app.models.sentiment_score import SentimentScore
from app.models.tweet import Tweet
from app.models.reddit_post import RedditPost


@pytest.fixture
def sample_tweet(db: Session):
    """Create a sample tweet for testing."""
    tweet = Tweet(
        tweet_id="test_tweet_1",
        text="This is a test tweet about a great game!",
        created_at=datetime.utcnow(),
        retweet_count=5,
        like_count=10,
        match_id=1,
        source="twitter"
    )
    db.add(tweet)
    db.commit()
    db.refresh(tweet)
    return tweet


@pytest.fixture
def sample_tweets(db: Session):
    """Create sample tweets for batch testing."""
    tweets = []
    texts = [
        "I love this team! Best performance ever!",
        "Terrible game today. Worst performance.",
        "It was an okay match. Nothing special.",
        "Amazing comeback! What a victory!",
        "Disappointed with the result."
    ]
    for i, text in enumerate(texts):
        tweet = Tweet(
            tweet_id=f"test_tweet_{i}",
            text=text,
            created_at=datetime.utcnow(),
            retweet_count=i * 2,
            like_count=i * 3,
            match_id=1,
            source="twitter"
        )
        db.add(tweet)
        tweets.append(tweet)
    db.commit()
    for tweet in tweets:
        db.refresh(tweet)
    return tweets


@pytest.fixture
def sample_reddit_post(db: Session):
    """Create a sample Reddit post for testing."""
    post = RedditPost(
        post_id="test_post_1",
        title="This is a test post about a great game!",
        text="The team played amazingly well today!",
        upvotes=15,
        created_at=datetime.utcnow(),
        match_id=1,
        subreddit="test_subreddit",
        source="reddit"
    )
    db.add(post)
    db.commit()
    db.refresh(post)
    return post


@pytest.fixture
def sample_reddit_posts(db: Session):
    """Create sample Reddit posts for batch testing."""
    posts = []
    titles = [
        "Great game today!",
        "Terrible performance",
        "Okay match",
        "Amazing victory",
        "Disappointed result"
    ]
    texts = [
        "The team was amazing!",
        "Worst game ever",
        "Nothing special",
        "What a comeback!",
        "Not good enough"
    ]
    for i, (title, text) in enumerate(zip(titles, texts)):
        post = RedditPost(
            post_id=f"test_post_{i}",
            title=title,
            text=text,
            upvotes=i * 5,
            created_at=datetime.utcnow(),
            match_id=1,
            subreddit="test_subreddit",
            source="reddit"
        )
        db.add(post)
        posts.append(post)
    db.commit()
    for post in posts:
        db.refresh(post)
    return posts


class TestProcessTweetSentiment:
    """Test processing single tweet sentiment."""

    def test_process_tweet_sentiment_creates_record(self, db: Session, sample_tweet: Tweet):
        """Test that processing tweet sentiment creates a database record."""
        sentiment = process_tweet_sentiment(db, sample_tweet.tweet_id, sample_tweet.text)

        assert sentiment.id is not None
        assert sentiment.entity_id == sample_tweet.tweet_id
        assert sentiment.entity_type == 'tweet'
        assert sentiment.sentiment_type in ['positive', 'negative', 'neutral']
        assert -1 <= sentiment.score <= 1
        assert 0 <= sentiment.positive <= 1
        assert 0 <= sentiment.negative <= 1
        assert 0 <= sentiment.neutral <= 1

    def test_process_tweet_sentiment_positive(self, db: Session, sample_tweet: Tweet):
        """Test processing positive tweet sentiment."""
        positive_text = "I absolutely love this team! Best performance ever!"
        sentiment = process_tweet_sentiment(db, "test_pos", positive_text)

        assert sentiment.sentiment_type == 'positive'
        assert sentiment.score > 0.05

    def test_process_tweet_sentiment_negative(self, db: Session, sample_tweet: Tweet):
        """Test processing negative tweet sentiment."""
        negative_text = "This is terrible! Worst performance ever!"
        sentiment = process_tweet_sentiment(db, "test_neg", negative_text)

        assert sentiment.sentiment_type == 'negative'
        assert sentiment.score < -0.05


class TestProcessTweetBatch:
    """Test batch processing of tweet sentiments."""

    def test_process_tweet_batch(self, db: Session, sample_tweets: list[Tweet]):
        """Test processing multiple tweets in batch."""
        sentiments = process_tweet_batch(db, sample_tweets)

        assert len(sentiments) == 5
        assert all(s.entity_type == 'tweet' for s in sentiments)
        assert all(s.id is not None for s in sentiments)

    def test_process_tweet_batch_empty(self, db: Session):
        """Test processing empty batch."""
        sentiments = process_tweet_batch(db, [])
        assert sentiments == []

    def test_process_tweet_batch_sentiments_calculated(self, db: Session, sample_tweets: list[Tweet]):
        """Test that sentiments are correctly calculated for batch."""
        sentiments = process_tweet_batch(db, sample_tweets)

        # Check that at least one positive and one negative sentiment exists
        sentiment_types = [s.sentiment_type for s in sentiments]
        assert 'positive' in sentiment_types
        assert 'negative' in sentiment_types


class TestProcessRedditPostSentiment:
    """Test processing single Reddit post sentiment."""

    def test_process_reddit_post_sentiment_creates_record(self, db: Session, sample_reddit_post: RedditPost):
        """Test that processing Reddit post sentiment creates a database record."""
        sentiment = process_reddit_post_sentiment(
            db,
            sample_reddit_post.post_id,
            f"{sample_reddit_post.title} {sample_reddit_post.text}"
        )

        assert sentiment.id is not None
        assert sentiment.entity_id == sample_reddit_post.post_id
        assert sentiment.entity_type == 'reddit_post'
        assert sentiment.sentiment_type in ['positive', 'negative', 'neutral']


class TestProcessRedditPostBatch:
    """Test batch processing of Reddit post sentiments."""

    def test_process_reddit_post_batch(self, db: Session, sample_reddit_posts: list[RedditPost]):
        """Test processing multiple Reddit posts in batch."""
        sentiments = process_reddit_post_batch(db, sample_reddit_posts)

        assert len(sentiments) == 5
        assert all(s.entity_type == 'reddit_post' for s in sentiments)
        assert all(s.id is not None for s in sentiments)

    def test_process_reddit_post_batch_empty(self, db: Session):
        """Test processing empty batch."""
        sentiments = process_reddit_post_batch(db, [])
        assert sentiments == []


class TestGetSentimentByEntity:
    """Test retrieving sentiment by entity."""

    def test_get_sentiment_by_entity_found(self, db: Session, sample_tweet: Tweet):
        """Test retrieving existing sentiment by entity."""
        process_tweet_sentiment(db, sample_tweet.tweet_id, sample_tweet.text)

        sentiment = get_sentiment_by_entity(
            db,
            sample_tweet.tweet_id,
            'tweet'
        )

        assert sentiment is not None
        assert sentiment.entity_id == sample_tweet.tweet_id

    def test_get_sentiment_by_entity_not_found(self, db: Session):
        """Test retrieving non-existent sentiment."""
        sentiment = get_sentiment_by_entity(db, "nonexistent_id", "tweet")
        assert sentiment is None


class TestGetSentimentsByMatch:
    """Test retrieving sentiments by match."""

    def test_get_sentiments_by_match(self, db: Session, sample_tweets: list[Tweet]):
        """Test retrieving all sentiments for a match."""
        process_tweet_batch(db, sample_tweets)

        sentiments = get_sentiments_by_match(db, 1)

        assert len(sentiments) == 5
        assert all(s.entity_type == 'tweet' for s in sentiments)

    def test_get_sentiments_by_match_empty(self, db: Session):
        """Test retrieving sentiments for match with no data."""
        sentiments = get_sentiments_by_match(db, 999)
        assert sentiments == []


class TestCalculateMatchSentimentMetrics:
    """Test calculating sentiment metrics for a match."""

    def test_calculate_match_sentiment_metrics(self, db: Session, sample_tweets: list[Tweet]):
        """Test calculating metrics for a match."""
        process_tweet_batch(db, sample_tweets)

        metrics = calculate_match_sentiment_metrics(db, 1)

        assert metrics['match_id'] == 1
        assert metrics['total_count'] == 5
        assert metrics['positive_count'] + metrics['negative_count'] + metrics['neutral_count'] == 5
        assert metrics['positive_ratio'] + metrics['negative_ratio'] + metrics['neutral_ratio'] == 1.0
        assert -1 <= metrics['average_compound'] <= 1

    def test_calculate_match_sentiment_metrics_empty(self, db: Session):
        """Test calculating metrics for match with no data."""
        metrics = calculate_match_sentiment_metrics(db, 999)

        assert metrics['match_id'] == 999
        assert metrics['total_count'] == 0
        assert metrics['average_compound'] == 0.0


class TestGetGlobalSentimentMetrics:
    """Test calculating global sentiment metrics."""

    def test_get_global_sentiment_metrics(self, db: Session, sample_tweets: list[Tweet]):
        """Test calculating global metrics."""
        process_tweet_batch(db, sample_tweets)

        metrics = get_global_sentiment_metrics(db)

        assert metrics['total_count'] == 5
        assert metrics['positive_count'] + metrics['negative_count'] + metrics['neutral_count'] == 5
        assert metrics['positive_ratio'] + metrics['negative_ratio'] + metrics['neutral_ratio'] == 1.0

    def test_get_global_sentiment_metrics_empty(self, db: Session):
        """Test calculating global metrics with no data."""
        metrics = get_global_sentiment_metrics(db)

        assert metrics['total_count'] == 0
        assert metrics['average_compound'] == 0.0
