"""Tests for citation verification."""

from __future__ import annotations

from collections.abc import Sequence
from typing import cast
from uuid import uuid4

import pytest

from noteflow.domain.entities import ActionItem, KeyPoint, Segment, Summary
from noteflow.domain.value_objects import MeetingId
from noteflow.infrastructure.summarization import SegmentCitationVerifier


def _segment(segment_id: int, text: str = "Test") -> Segment:
    """Create a test segment."""
    return Segment(
        segment_id=segment_id,
        text=text,
        start_time=segment_id * 5.0,
        end_time=(segment_id + 1) * 5.0,
    )


def _key_point(text: str, segment_ids: list[int]) -> KeyPoint:
    """Create a test key point."""
    return KeyPoint(text=text, segment_ids=segment_ids)


def _action_item(text: str, segment_ids: list[int]) -> ActionItem:
    """Create a test action item."""
    return ActionItem(text=text, segment_ids=segment_ids)


def _summary(
    key_points: list[KeyPoint] | None = None,
    action_items: list[ActionItem] | None = None,
) -> Summary:
    """Create a test summary."""
    return Summary(
        meeting_id=MeetingId(uuid4()),
        executive_summary="Test summary",
        key_points=key_points or [],
        action_items=action_items or [],
    )


class TestSegmentCitationVerifier:
    """Tests for SegmentCitationVerifier."""

    @pytest.fixture
    def verifier(self) -> SegmentCitationVerifier:
        """Create verifier instance."""
        return SegmentCitationVerifier()

    @pytest.mark.parametrize(
        "attr,expected",
        [
            ("is_valid", True),
            ("invalid_key_point_indices", ()),
            ("invalid_action_item_indices", ()),
            ("missing_segment_ids", ()),
        ],
    )
    def test_verify_valid_citations(
        self, verifier: SegmentCitationVerifier, attr: str, expected: object
    ) -> None:
        """All citations valid should return is_valid=True."""
        segments = [_segment(0), _segment(1), _segment(2)]
        summary = _summary(
            key_points=[_key_point("Point 1", [0, 1])],
            action_items=[_action_item("Action 1", [2])],
        )
        result = verifier.verify_citations(summary, segments)
        assert getattr(result, attr) == expected, f"result.{attr} should be {expected!r}"

    @pytest.mark.parametrize(
        "attr,expected",
        [
            ("is_valid", False),
            ("invalid_key_point_indices", (0,)),
            ("invalid_action_item_indices", ()),
            ("missing_segment_ids", (99,)),
        ],
    )
    def test_verify_invalid_key_point_citation(
        self, verifier: SegmentCitationVerifier, attr: str, expected: object
    ) -> None:
        """Invalid segment_id in key point should be detected."""
        segments = [_segment(0), _segment(1)]
        summary = _summary(
            key_points=[_key_point("Point 1", [0, 99])],  # 99 doesn't exist
        )
        result = verifier.verify_citations(summary, segments)
        assert getattr(result, attr) == expected, f"result.{attr} should be {expected!r}"

    @pytest.mark.parametrize(
        "attr,expected",
        [
            ("is_valid", False),
            ("invalid_key_point_indices", ()),
            ("invalid_action_item_indices", (0,)),
            ("missing_segment_ids", (50,)),
        ],
    )
    def test_verify_invalid_action_item_citation(
        self, verifier: SegmentCitationVerifier, attr: str, expected: object
    ) -> None:
        """Invalid segment_id in action item should be detected."""
        segments = [_segment(0), _segment(1)]
        summary = _summary(
            action_items=[_action_item("Action 1", [50])],  # 50 doesn't exist
        )
        result = verifier.verify_citations(summary, segments)
        assert getattr(result, attr) == expected, f"result.{attr} should be {expected!r}"

    @pytest.mark.parametrize(
        "attr,expected",
        [
            ("is_valid", False),
            ("invalid_key_point_indices", (1, 2)),
            ("invalid_action_item_indices", (0,)),
            ("missing_segment_ids", (1, 2, 3)),
        ],
    )
    def test_verify_multiple_invalid_citations(
        self, verifier: SegmentCitationVerifier, attr: str, expected: object
    ) -> None:
        """Multiple invalid citations should all be detected."""
        segments = [_segment(0)]
        summary = _summary(
            key_points=[
                _key_point("Point 1", [0]),
                _key_point("Point 2", [1]),  # Invalid
                _key_point("Point 3", [2]),  # Invalid
            ],
            action_items=[
                _action_item("Action 1", [3]),  # Invalid
            ],
        )
        result = verifier.verify_citations(summary, segments)
        assert getattr(result, attr) == expected, f"result.{attr} should be {expected!r}"

    def test_verify_empty_summary(self, verifier: SegmentCitationVerifier) -> None:
        """Empty summary should be valid."""
        segments = [_segment(0)]
        summary = _summary()

        result = verifier.verify_citations(summary, segments)

        assert result.is_valid is True, "empty summary should be valid"

    def test_verify_empty_segments(self, verifier: SegmentCitationVerifier) -> None:
        """Summary with citations but no segments should be invalid."""
        segments: list[Segment] = []
        summary = _summary(key_points=[_key_point("Point 1", [0])])

        result = verifier.verify_citations(summary, segments)

        assert result.is_valid is False, "citations without matching segments are invalid"
        assert result.missing_segment_ids == (0,), "should report missing segment 0"

    def test_verify_empty_citations(self, verifier: SegmentCitationVerifier) -> None:
        """Key points/actions with empty segment_ids should be valid."""
        segments = [_segment(0)]
        summary = _summary(
            key_points=[_key_point("Point 1", [])],  # No citations
            action_items=[_action_item("Action 1", [])],  # No citations
        )

        result = verifier.verify_citations(summary, segments)

        assert result.is_valid is True, "empty segment_ids should be valid"

    def test_invalid_count_property(self, verifier: SegmentCitationVerifier) -> None:
        """invalid_count should sum key point and action item invalid counts."""
        segments = [_segment(0)]
        summary = _summary(
            key_points=[
                _key_point("Point 1", [1]),  # Invalid
                _key_point("Point 2", [2]),  # Invalid
            ],
            action_items=[
                _action_item("Action 1", [3]),  # Invalid
            ],
        )

        result = verifier.verify_citations(summary, segments)

        assert result.invalid_count == 3, "should count all invalid items"


class TestFilterInvalidCitations:
    """Tests for filter_invalid_citations method."""

    @pytest.fixture
    def verifier(self) -> SegmentCitationVerifier:
        """Create verifier instance."""
        return SegmentCitationVerifier()

    def test_filter_removes_invalid_segment_ids(self, verifier: SegmentCitationVerifier) -> None:
        """Invalid segment_ids should be removed from citations."""
        segments = [_segment(0), _segment(1)]
        summary = _summary(
            key_points=[_key_point("Point 1", [0, 1, 99])],  # 99 invalid
            action_items=[_action_item("Action 1", [1, 50])],  # 50 invalid
        )

        filtered = verifier.filter_invalid_citations(summary, segments)

        assert filtered.key_points[0].segment_ids == [0, 1], "should remove invalid 99"
        assert filtered.action_items[0].segment_ids == [1], "should remove invalid 50"

    def test_filter_preserves_valid_citations(self, verifier: SegmentCitationVerifier) -> None:
        """Valid citations should be preserved."""
        segments = [_segment(0), _segment(1), _segment(2)]
        summary = _summary(
            key_points=[_key_point("Point 1", [0, 1])],
            action_items=[_action_item("Action 1", [2])],
        )

        filtered = verifier.filter_invalid_citations(summary, segments)

        assert filtered.key_points[0].segment_ids == [0, 1], "valid key point citations preserved"
        assert filtered.action_items[0].segment_ids == [2], "valid action item citations preserved"

    @pytest.mark.parametrize(
        "attr_path,expected",
        [
            ("executive_summary", "Important meeting"),
            ("key_points[0].text", "Key point"),
            ("key_points[0].start_time", 1.0),
            ("action_items[0].assignee", "Alice"),
            ("action_items[0].priority", 2),
            ("model_version", "test/1.0"),
        ],
    )
    def test_filter_preserves_other_fields(
        self, verifier: SegmentCitationVerifier, attr_path: str, expected: object
    ) -> None:
        """Non-citation fields should be preserved."""
        segments = [_segment(0)]
        summary = Summary(
            meeting_id=MeetingId(uuid4()),
            executive_summary="Important meeting",
            key_points=[KeyPoint(text="Key point", segment_ids=[0], start_time=1.0, end_time=2.0)],
            action_items=[ActionItem(text="Action", segment_ids=[0], assignee="Alice", priority=2)],
            provider_name="test",
            model_name="1.0",
        )
        filtered = verifier.filter_invalid_citations(summary, segments)
        # Navigate the attribute path
        obj: object = filtered
        for part in attr_path.replace("[", ".").replace("]", "").split("."):
            obj = getattr(obj, part) if not part.isdigit() else cast(Sequence[object], obj)[int(part)]
        assert obj == expected, f"filtered.{attr_path} should be {expected!r}"
