"""Tests for Stage 2: LLM Classification."""

from __future__ import annotations

import pytest

from reviewiq_pipeline.services.llm_client import create_fallback_response
from reviewiq_pipeline.validation.validators import validate_stage2_output


class TestLLMClient:
    """Tests for LLM client functionality."""

    def test_fallback_response_structure(self):
        """Test that fallback response has correct structure."""
        review_text = "This is a test review."
        response = create_fallback_response(review_text)

        assert "spans" in response
        assert "review_summary" in response
        assert len(response["spans"]) == 1

        span = response["spans"][0]
        assert span["span_index"] == 0
        assert span["span_text"] == review_text
        assert span["span_start"] == 0
        assert span["span_end"] == len(review_text)
        assert span["is_primary"] is True
        assert span["confidence"] == "low"

    def test_fallback_response_valid_urt(self):
        """Test that fallback response has valid URT codes."""
        response = create_fallback_response("Test review")
        span = response["spans"][0]

        assert span["urt_primary"] == "O1.01"
        assert span["valence"] == "V0"
        assert span["intensity"] == "I1"


class TestStage2Validation:
    """Tests for Stage 2 validation."""

    def test_validate_valid_output(self, sample_stage2_output):
        """Test validation of valid Stage 2 output."""
        result = validate_stage2_output(sample_stage2_output)

        assert result["stage"] == "stage2"
        assert result["passed"] is True
        assert result["error_count"] == 0

    def test_validate_invalid_urt_code(self, sample_stage2_output):
        """Test validation catches invalid URT code."""
        sample_stage2_output["reviews_classified"][0]["urt_primary"] = "INVALID"

        result = validate_stage2_output(sample_stage2_output)

        assert result["passed"] is False
        assert any(e["rule"] == "V2.1" for e in result["errors"])

    def test_validate_too_many_secondary(self, sample_stage2_output):
        """Test validation catches too many secondary codes."""
        sample_stage2_output["reviews_classified"][0]["urt_secondary"] = [
            "O1.01", "O1.02", "O1.03"
        ]

        result = validate_stage2_output(sample_stage2_output)

        assert result["passed"] is False
        assert any(e["rule"] == "V2.2" for e in result["errors"])

    def test_validate_invalid_valence(self, sample_stage2_output):
        """Test validation catches invalid valence."""
        sample_stage2_output["reviews_classified"][0]["valence"] = "INVALID"

        result = validate_stage2_output(sample_stage2_output)

        assert result["passed"] is False
        assert any(e["rule"] == "V2.3" for e in result["errors"])

    def test_validate_invalid_trust_score(self, sample_stage2_output):
        """Test validation catches trust score out of bounds."""
        sample_stage2_output["reviews_classified"][0]["trust_score"] = 0.1

        result = validate_stage2_output(sample_stage2_output)

        assert result["passed"] is False
        assert any(e["rule"] == "V2.9" for e in result["errors"])

    def test_validate_invalid_embedding_dim(self, sample_stage2_output):
        """Test validation catches wrong embedding dimension."""
        sample_stage2_output["reviews_classified"][0]["embedding"] = [0.1] * 100

        result = validate_stage2_output(sample_stage2_output)

        assert result["passed"] is False
        assert any(e["rule"] == "V2.10" for e in result["errors"])

    def test_validate_multiple_primaries(self, sample_stage2_output):
        """Test validation catches multiple primary spans."""
        for span in sample_stage2_output["reviews_classified"][0]["spans"]:
            span["is_primary"] = True

        result = validate_stage2_output(sample_stage2_output)

        assert result["passed"] is False
        assert any(e["rule"] == "V2.8" for e in result["errors"])

    def test_validate_no_primary(self, sample_stage2_output):
        """Test validation catches no primary span."""
        for span in sample_stage2_output["reviews_classified"][0]["spans"]:
            span["is_primary"] = False

        result = validate_stage2_output(sample_stage2_output)

        assert result["passed"] is False
        assert any(e["rule"] == "V2.8" for e in result["errors"])

    def test_validate_invalid_span_bounds(self, sample_stage2_output):
        """Test validation catches invalid span bounds."""
        sample_stage2_output["reviews_classified"][0]["spans"][0]["span_start"] = 100
        sample_stage2_output["reviews_classified"][0]["spans"][0]["span_end"] = 50

        result = validate_stage2_output(sample_stage2_output)

        assert result["passed"] is False
        assert any(e["rule"] == "V2.5" for e in result["errors"])


class TestSpanExtraction:
    """Tests for span extraction logic."""

    def test_primary_span_selection_by_intensity(self):
        """Test that primary span is selected by highest intensity."""
        from reviewiq_pipeline.stages.stage2_classify import Stage2Classifier
        from reviewiq_pipeline.config import Config

        config = Config()
        classifier = Stage2Classifier(config)

        spans = [
            {
                "span_id": "span1",
                "span_index": 0,
                "valence": "V-",
                "intensity": "I1",
                "is_primary": False,
            },
            {
                "span_id": "span2",
                "span_index": 1,
                "valence": "V-",
                "intensity": "I3",
                "is_primary": False,
            },
        ]

        result = classifier._ensure_primary_span(spans)

        # Span with I3 should be primary
        assert result[1]["is_primary"] is True
        assert result[0]["is_primary"] is False

    def test_primary_span_selection_by_valence(self):
        """Test that negative valence is preferred over positive."""
        from reviewiq_pipeline.stages.stage2_classify import Stage2Classifier
        from reviewiq_pipeline.config import Config

        config = Config()
        classifier = Stage2Classifier(config)

        spans = [
            {
                "span_id": "span1",
                "span_index": 0,
                "valence": "V+",
                "intensity": "I2",
                "is_primary": False,
            },
            {
                "span_id": "span2",
                "span_index": 1,
                "valence": "V-",
                "intensity": "I2",
                "is_primary": False,
            },
        ]

        result = classifier._ensure_primary_span(spans)

        # Span with V- should be primary
        assert result[1]["is_primary"] is True
        assert result[0]["is_primary"] is False
