"""
Tests for Sentiment Analyzer

This module tests the VADER sentiment analyzer functionality.
"""

import pytest
from app.ml.sentiment_analyzer import (
    analyze_sentiment,
    analyze_sentiment_batch,
    calculate_aggregated_metrics,
    classify_sentiment,
    test_analyzer_performance
)


class TestClassifySentiment:
    """Test sentiment classification based on compound score."""

    def test_positive_classification(self):
        """Test classification of positive sentiment."""
        assert classify_sentiment(0.5) == 'positive'
        assert classify_sentiment(0.05) == 'positive'
        assert classify_sentiment(1.0) == 'positive'

    def test_negative_classification(self):
        """Test classification of negative sentiment."""
        assert classify_sentiment(-0.5) == 'negative'
        assert classify_sentiment(-0.05) == 'negative'
        assert classify_sentiment(-1.0) == 'negative'

    def test_neutral_classification(self):
        """Test classification of neutral sentiment."""
        assert classify_sentiment(0.0) == 'neutral'
        assert classify_sentiment(0.04) == 'neutral'
        assert classify_sentiment(-0.04) == 'neutral'


class TestAnalyzeSentiment:
    """Test single text sentiment analysis."""

    def test_positive_text(self):
        """Test analysis of positive text."""
        text = "I love this game! It's absolutely amazing and wonderful!"
        result = analyze_sentiment(text)

        assert result['sentiment'] == 'positive'
        assert result['compound'] > 0.05
        assert 0 <= result['positive'] <= 1
        assert 0 <= result['negative'] <= 1
        assert 0 <= result['neutral'] <= 1

    def test_negative_text(self):
        """Test analysis of negative text."""
        text = "This is terrible! I hate it and it's the worst ever."
        result = analyze_sentiment(text)

        assert result['sentiment'] == 'negative'
        assert result['compound'] < -0.05
        assert 0 <= result['positive'] <= 1
        assert 0 <= result['negative'] <= 1
        assert 0 <= result['neutral'] <= 1

    def test_neutral_text(self):
        """Test analysis of neutral text."""
        text = "The game is okay. Nothing special but nothing bad."
        result = analyze_sentiment(text)

        assert result['sentiment'] in ['positive', 'negative', 'neutral']
        assert 0 <= result['compound'] <= 1
        assert 0 <= result['positive'] <= 1
        assert 0 <= result['negative'] <= 1
        assert 0 <= result['neutral'] <= 1

    def test_empty_text_raises_error(self):
        """Test that empty text raises ValueError."""
        with pytest.raises(ValueError):
            analyze_sentiment("")

    def test_invalid_input_raises_error(self):
        """Test that invalid input raises ValueError."""
        with pytest.raises(ValueError):
            analyze_sentiment(None)

        with pytest.raises(ValueError):
            analyze_sentiment(123)


class TestAnalyzeSentimentBatch:
    """Test batch sentiment analysis."""

    def test_batch_analysis_multiple_texts(self):
        """Test analysis of multiple texts."""
        texts = [
            "I love this!",
            "This is terrible!",
            "It's okay."
        ]
        results = analyze_sentiment_batch(texts)

        assert len(results) == 3
        assert all('compound' in r for r in results)
        assert all('positive' in r for r in results)
        assert all('negative' in r for r in results)
        assert all('neutral' in r for r in results)
        assert all('sentiment' in r for r in results)

    def test_batch_analysis_empty_list(self):
        """Test batch analysis with empty list."""
        results = analyze_sentiment_batch([])
        assert results == []

    def test_batch_analysis_with_errors(self):
        """Test batch analysis handles errors gracefully."""
        texts = [
            "This is good!",
            "",  # Invalid - should be handled gracefully
            "This is great!"
        ]
        results = analyze_sentiment_batch(texts)

        assert len(results) == 3
        # The invalid text should return neutral sentiment
        assert results[1]['sentiment'] == 'neutral'
        assert results[1]['compound'] == 0.0


class TestCalculateAggregatedMetrics:
    """Test calculation of aggregated sentiment metrics."""

    def test_aggregated_metrics_empty_list(self):
        """Test aggregated metrics with empty list."""
        metrics = calculate_aggregated_metrics([])

        assert metrics['total_count'] == 0
        assert metrics['positive_count'] == 0
        assert metrics['negative_count'] == 0
        assert metrics['neutral_count'] == 0
        assert metrics['positive_ratio'] == 0.0
        assert metrics['negative_ratio'] == 0.0
        assert metrics['neutral_ratio'] == 0.0
        assert metrics['average_compound'] == 0.0

    def test_aggregated_metrics_all_positive(self):
        """Test aggregated metrics with all positive sentiments."""
        sentiments = [
            {'compound': 0.5, 'sentiment': 'positive'},
            {'compound': 0.7, 'sentiment': 'positive'},
            {'compound': 0.9, 'sentiment': 'positive'}
        ]
        metrics = calculate_aggregated_metrics(sentiments)

        assert metrics['total_count'] == 3
        assert metrics['positive_count'] == 3
        assert metrics['negative_count'] == 0
        assert metrics['neutral_count'] == 0
        assert metrics['positive_ratio'] == 1.0
        assert metrics['average_compound'] == pytest.approx(0.7, rel=0.01)

    def test_aggregated_metrics_mixed(self):
        """Test aggregated metrics with mixed sentiments."""
        sentiments = [
            {'compound': 0.5, 'sentiment': 'positive'},
            {'compound': -0.5, 'sentiment': 'negative'},
            {'compound': 0.0, 'sentiment': 'neutral'},
            {'compound': 0.7, 'sentiment': 'positive'}
        ]
        metrics = calculate_aggregated_metrics(sentiments)

        assert metrics['total_count'] == 4
        assert metrics['positive_count'] == 2
        assert metrics['negative_count'] == 1
        assert metrics['neutral_count'] == 1
        assert metrics['positive_ratio'] == 0.5
        assert metrics['negative_ratio'] == 0.25
        assert metrics['neutral_ratio'] == 0.25
        assert metrics['average_compound'] == pytest.approx(0.175, rel=0.01)


class TestAnalyzerPerformance:
    """Test performance of the sentiment analyzer."""

    def test_performance_1000_tweets(self):
        """Test that 1000 tweets can be analyzed in less than 1 second."""
        time_taken = test_analyzer_performance(1000)

        assert time_taken < 1.0, f"Analysis took {time_taken:.4f}s, expected < 1.0s"

    def test_performance_5000_tweets(self):
        """Test performance with 5000 tweets."""
        time_taken = test_analyzer_performance(5000)

        # Should still be fast, but we allow some scaling
        assert time_taken < 5.0, f"Analysis took {time_taken:.4f}s, expected < 5.0s"

    def test_performance_10000_tweets(self):
        """Test performance with 10000 tweets."""
        time_taken = test_analyzer_performance(10000)

        # Should still be very fast
        assert time_taken < 10.0, f"Analysis took {time_taken:.4f}s, expected < 10.0s"
