"""Tests for Summary, KeyPoint, and ActionItem entities."""

from __future__ import annotations

from datetime import datetime

import pytest

from noteflow.domain.entities.summary import ActionItem, KeyPoint, Summary
from noteflow.domain.value_objects import MeetingId

# Test constants
MANY_SEGMENT_IDS_COUNT = 50
KEY_POINT_START_TIME = 10.5
KEY_POINT_END_TIME = 25.0
VERY_LONG_SUMMARY_LENGTH = 10000


class TestKeyPoint:
    """Tests for KeyPoint entity."""

    @pytest.mark.parametrize(
        "attr,expected",
        [
            ("text", "Important discussion about architecture"),
            ("segment_ids", []),
            ("start_time", 0.0),
            ("end_time", 0.0),
        ],
    )
    def test_key_point_defaults(self, attr: str, expected: object) -> None:
        """Test KeyPoint default attribute values."""
        kp = KeyPoint(text="Important discussion about architecture")
        assert getattr(kp, attr) == expected, f"expected {attr}={expected!r}, got {getattr(kp, attr)!r}"

    def test_key_point_is_sourced_false(self) -> None:
        """Test is_sourced returns False when no segment_ids."""
        kp = KeyPoint(text="No evidence")
        assert kp.is_sourced() is False, "key point without segment_ids should not be sourced"

    def test_key_point_is_sourced_true(self) -> None:
        """Test is_sourced returns True with segment_ids."""
        kp = KeyPoint(text="With evidence", segment_ids=[1, 2, 3])
        assert kp.is_sourced() is True, "key point with segment_ids should be sourced"

    def test_key_point_with_timing(self) -> None:
        """Test KeyPoint with timing information."""
        kp = KeyPoint(
            text="Timed point",
            segment_ids=[0, 1],
            start_time=KEY_POINT_START_TIME,
            end_time=KEY_POINT_END_TIME,
        )
        assert kp.start_time == KEY_POINT_START_TIME, "start_time should match provided value"
        assert kp.end_time == KEY_POINT_END_TIME, "end_time should match provided value"


class TestActionItem:
    """Tests for ActionItem entity."""

    @pytest.mark.parametrize(
        "attr,expected",
        [
            ("text", "Review PR #123"),
            ("assignee", ""),
            ("due_date", None),
            ("priority", 0),
            ("segment_ids", []),
        ],
    )
    def test_action_item_defaults(self, attr: str, expected: object) -> None:
        """Test ActionItem default attribute values."""
        ai = ActionItem(text="Review PR #123")
        assert getattr(ai, attr) == expected, f"expected {attr}={expected!r}, got {getattr(ai, attr)!r}"

    def test_action_item_has_evidence_false(self) -> None:
        """Test has_evidence returns False when no segment_ids."""
        ai = ActionItem(text="Task without evidence")
        assert ai.has_evidence() is False, "action item without segment_ids should not have evidence"

    def test_action_item_has_evidence_true(self) -> None:
        """Test has_evidence returns True with segment_ids."""
        ai = ActionItem(text="Task with evidence", segment_ids=[5])
        assert ai.has_evidence() is True, "action item with segment_ids should have evidence"

    def test_action_item_is_assigned_false(self) -> None:
        """Test is_assigned returns False when no assignee."""
        ai = ActionItem(text="Unassigned task")
        assert ai.is_assigned() is False, "action item without assignee should not be assigned"

    def test_action_item_is_assigned_true(self) -> None:
        """Test is_assigned returns True with assignee."""
        ai = ActionItem(text="Assigned task", assignee="Alice")
        assert ai.is_assigned() is True, "action item with assignee should be assigned"

    def test_action_item_has_due_date_false(self) -> None:
        """Test has_due_date returns False when no due_date."""
        ai = ActionItem(text="No deadline")
        assert ai.has_due_date() is False, "action item without due_date should not have due date"

    def test_action_item_has_due_date_true(self) -> None:
        """Test has_due_date returns True with due_date."""
        ai = ActionItem(text="With deadline", due_date=datetime(2024, 12, 31))
        assert ai.has_due_date() is True, "action item with due_date should have due date"


class TestSummary:
    """Tests for Summary entity."""

    @pytest.mark.parametrize(
        "attr,expected",
        [
            ("executive_summary", ""),
            ("key_points", []),
            ("action_items", []),
            ("generated_at", None),
            ("model_version", ""),
        ],
    )
    def test_summary_defaults(self, meeting_id: MeetingId, attr: str, expected: object) -> None:
        """Test Summary default attribute values."""
        summary = Summary(meeting_id=meeting_id)
        assert getattr(summary, attr) == expected, f"expected {attr}={expected!r}, got {getattr(summary, attr)!r}"

    def test_summary_meeting_id(self, meeting_id: MeetingId) -> None:
        """Test Summary stores meeting_id correctly."""
        summary = Summary(meeting_id=meeting_id)
        assert summary.meeting_id == meeting_id, f"expected meeting_id={meeting_id}, got {summary.meeting_id}"

    def test_summary_key_point_count(self, meeting_id: MeetingId) -> None:
        """Test key_point_count property."""
        summary = Summary(
            meeting_id=meeting_id,
            key_points=[
                KeyPoint(text="Point 1"),
                KeyPoint(text="Point 2"),
                KeyPoint(text="Point 3"),
            ],
        )
        assert summary.key_point_count == 3, f"expected key_point_count=3, got {summary.key_point_count}"

    def test_summary_action_item_count(self, meeting_id: MeetingId) -> None:
        """Test action_item_count property."""
        summary = Summary(
            meeting_id=meeting_id,
            action_items=[
                ActionItem(text="Task 1"),
                ActionItem(text="Task 2"),
            ],
        )
        assert summary.action_item_count == 2, f"expected action_item_count=2, got {summary.action_item_count}"

    def test_all_points_have_evidence_true(self, meeting_id: MeetingId) -> None:
        """Test all_points_have_evidence returns True when all evidenced."""
        summary = Summary(
            meeting_id=meeting_id,
            key_points=[
                KeyPoint(text="Point 1", segment_ids=[0]),
                KeyPoint(text="Point 2", segment_ids=[1, 2]),
            ],
        )
        assert summary.all_points_have_evidence() is True, "all points with segment_ids should be evidenced"

    def test_all_points_have_evidence_false(self, meeting_id: MeetingId) -> None:
        """Test all_points_have_evidence returns False when some unevidenced."""
        summary = Summary(
            meeting_id=meeting_id,
            key_points=[
                KeyPoint(text="Point 1", segment_ids=[0]),
                KeyPoint(text="Point 2"),  # No evidence
            ],
        )
        assert summary.all_points_have_evidence() is False, "should be False when some points lack segment_ids"

    def test_all_actions_have_evidence_true(self, meeting_id: MeetingId) -> None:
        """Test all_actions_have_evidence returns True when all evidenced."""
        summary = Summary(
            meeting_id=meeting_id,
            action_items=[
                ActionItem(text="Task 1", segment_ids=[0]),
            ],
        )
        assert summary.all_actions_have_evidence() is True, "all actions with segment_ids should be evidenced"

    def test_all_actions_have_evidence_false(self, meeting_id: MeetingId) -> None:
        """Test all_actions_have_evidence returns False when some unevidenced."""
        summary = Summary(
            meeting_id=meeting_id,
            action_items=[
                ActionItem(text="Task 1"),  # No evidence
            ],
        )
        assert summary.all_actions_have_evidence() is False, "should be False when some actions lack segment_ids"

    def test_is_fully_evidenced_true(self, meeting_id: MeetingId) -> None:
        """Test is_fully_evidenced returns True when all items evidenced."""
        summary = Summary(
            meeting_id=meeting_id,
            key_points=[KeyPoint(text="KP", segment_ids=[0])],
            action_items=[ActionItem(text="AI", segment_ids=[1])],
        )
        assert summary.is_fully_evidenced() is True, "summary with all evidenced items should be fully evidenced"

    def test_is_fully_evidenced_false_points(self, meeting_id: MeetingId) -> None:
        """Test is_fully_evidenced returns False with unevidenced points."""
        summary = Summary(
            meeting_id=meeting_id,
            key_points=[KeyPoint(text="KP")],  # No evidence
            action_items=[ActionItem(text="AI", segment_ids=[1])],
        )
        assert summary.is_fully_evidenced() is False, "summary with unevidenced points should not be fully evidenced"

    def test_unevidenced_points(self, meeting_id: MeetingId) -> None:
        """Test unevidenced_points property filters correctly."""
        kp_no_evidence = KeyPoint(text="No evidence")
        kp_with_evidence = KeyPoint(text="With evidence", segment_ids=[0])
        summary = Summary(
            meeting_id=meeting_id,
            key_points=[kp_no_evidence, kp_with_evidence],
        )
        unevidenced = summary.unevidenced_points
        assert len(unevidenced) == 1, "should have exactly one unevidenced point"
        assert unevidenced[0] == kp_no_evidence, "unevidenced list should contain the point without segment_ids"

    def test_unevidenced_actions(self, meeting_id: MeetingId) -> None:
        """Test unevidenced_actions property filters correctly."""
        ai_no_evidence = ActionItem(text="No evidence")
        ai_with_evidence = ActionItem(text="With evidence", segment_ids=[0])
        summary = Summary(
            meeting_id=meeting_id,
            action_items=[ai_no_evidence, ai_with_evidence],
        )
        unevidenced = summary.unevidenced_actions
        assert len(unevidenced) == 1, "should have exactly one unevidenced action"
        assert unevidenced[0] == ai_no_evidence, "unevidenced list should contain the action without segment_ids"


class TestSummaryEdgeCases:
    """Edge case tests for Summary entity."""

    def test_all_points_have_evidence_empty_list(self, meeting_id: MeetingId) -> None:
        """Test all_points_have_evidence returns True for empty key_points."""
        summary = Summary(meeting_id=meeting_id, key_points=[])
        assert summary.all_points_have_evidence() is True, "empty key_points list should be considered all evidenced"

    def test_all_actions_have_evidence_empty_list(self, meeting_id: MeetingId) -> None:
        """Test all_actions_have_evidence returns True for empty action_items."""
        summary = Summary(meeting_id=meeting_id, action_items=[])
        assert summary.all_actions_have_evidence() is True, "empty action_items list should be considered all evidenced"

    def test_is_fully_evidenced_empty_summary(self, meeting_id: MeetingId) -> None:
        """Test is_fully_evidenced returns True for empty summary."""
        summary = Summary(meeting_id=meeting_id)
        assert summary.is_fully_evidenced() is True, "empty summary should be considered fully evidenced"

    def test_all_points_unevidenced(self, meeting_id: MeetingId) -> None:
        """Test all_points_have_evidence returns False when all points unevidenced."""
        summary = Summary(
            meeting_id=meeting_id,
            key_points=[
                KeyPoint(text="Point 1"),
                KeyPoint(text="Point 2"),
                KeyPoint(text="Point 3"),
            ],
        )
        assert summary.all_points_have_evidence() is False, "should return False when no points have evidence"
        assert len(summary.unevidenced_points) == 3, "all three points should be unevidenced"

    def test_mixed_evidence_counts(self, meeting_id: MeetingId) -> None:
        """Test summary with mixed evidence states across points and actions."""
        summary = Summary(
            meeting_id=meeting_id,
            key_points=[
                KeyPoint(text="Evidenced point", segment_ids=[0]),
                KeyPoint(text="Unevidenced point"),
            ],
            action_items=[
                ActionItem(text="Evidenced action", segment_ids=[1]),
                ActionItem(text="Unevidenced action 1"),
                ActionItem(text="Unevidenced action 2"),
            ],
        )
        assert len(summary.unevidenced_points) == 1, "one point lacks evidence"
        assert len(summary.unevidenced_actions) == 2, "two actions lack evidence"
        assert summary.is_fully_evidenced() is False, "mixed evidence means not fully evidenced"

    def test_key_point_with_many_segment_ids(self) -> None:
        """Test key point with many segment references."""
        many_segments = list(range(MANY_SEGMENT_IDS_COUNT))
        kp = KeyPoint(text="Well-sourced point", segment_ids=many_segments)
        assert kp.is_sourced() is True, "key point with segments should be sourced"
        assert len(kp.segment_ids) == MANY_SEGMENT_IDS_COUNT, "all segment_ids should be preserved"

    def test_action_item_with_all_fields(self) -> None:
        """Test action item with all optional fields populated."""
        ai = ActionItem(
            text="Complete task",
            assignee="Alice",
            due_date=datetime(2024, 12, 31, 23, 59, 59),
            priority=1,
            segment_ids=[1, 2, 3],
        )
        assert ai.is_assigned() is True, "should be assigned when assignee provided"
        assert ai.has_due_date() is True, "should have due date when provided"
        assert ai.has_evidence() is True, "should have evidence when segment_ids provided"
        assert ai.priority == 1, "priority should match provided value"

    def test_summary_with_unicode_content(self, meeting_id: MeetingId) -> None:
        """Test summary handles unicode content."""
        summary = Summary(
            meeting_id=meeting_id,
            executive_summary="会议总结 🎯",
            key_points=[KeyPoint(text="重要讨论点")],
            action_items=[ActionItem(text="完成任务 ✅", assignee="张三")],
        )
        assert "会议总结" in summary.executive_summary, "executive_summary should contain unicode"
        assert summary.key_points[0].text == "重要讨论点", "key point text should preserve unicode"
        assert summary.action_items[0].assignee == "张三", "action item assignee should preserve unicode"

    def test_summary_very_long_executive_summary(self, meeting_id: MeetingId) -> None:
        """Test summary handles very long executive summary."""
        long_summary = "A" * VERY_LONG_SUMMARY_LENGTH
        summary = Summary(meeting_id=meeting_id, executive_summary=long_summary)
        assert len(summary.executive_summary) == VERY_LONG_SUMMARY_LENGTH, f"expected length {VERY_LONG_SUMMARY_LENGTH}, got {len(summary.executive_summary)}"
