"""Tests for the review engine and agents."""

from __future__ import annotations

import pytest

from pptx_mcp.agents.planner import PlannerInput, SlidePlannerAgent
from pptx_mcp.agents.designer import DesignAgent, DesignerInput
from pptx_mcp.agents.iterator import IterationAgent, IterationInput
from pptx_mcp.agents.reviewer import ReviewAgent, ReviewerInput
from pptx_mcp.brand.defaults import executive_default
from pptx_mcp.models.review import ReviewResult, Severity
from pptx_mcp.models.slides import (
    ColumnContent,
    PresentationDefinition,
    PresentationMetadata,
    SlideDefinition,
    SlideType,
)
from pptx_mcp.review.engine import ReviewEngine


@pytest.fixture
def brand():
    return executive_default()


@pytest.fixture
def review_engine(brand):
    return ReviewEngine(brand=brand)


def _make_definition(slides: list[SlideDefinition]) -> PresentationDefinition:
    return PresentationDefinition(
        metadata=PresentationMetadata(title="Review Test"),
        slides=slides,
    )


class TestReviewEngine:
    def test_clean_deck_scores_high(self, review_engine: ReviewEngine) -> None:
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Good Deck"),
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                title="Key Points",
                bullets=["Short point one", "Short point two"],
            ),
        ])
        result = review_engine.review_definition(definition)
        assert result.overall_score >= 7.0
        assert result.total_slides == 2

    def test_overloaded_slide_flagged(self, review_engine: ReviewEngine) -> None:
        definition = _make_definition([
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                title="Too Much Text",
                bullets=[f"This is bullet number {i} with some extra words to pad it out nicely" for i in range(10)],
            ),
        ])
        result = review_engine.review_definition(definition)
        content_issues = [i for i in result.issues if "bullet" in i.message.lower() or "word" in i.message.lower()]
        assert len(content_issues) > 0

    def test_empty_slide_flagged(self, review_engine: ReviewEngine) -> None:
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.CONTENT),
        ])
        result = review_engine.review_definition(definition)
        error_issues = [i for i in result.issues if i.severity == Severity.ERROR]
        assert len(error_issues) > 0

    def test_missing_title_flagged(self, review_engine: ReviewEngine) -> None:
        definition = _make_definition([
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                bullets=["No title on this slide"],
            ),
        ])
        result = review_engine.review_definition(definition)
        layout_issues = [i for i in result.issues if "title" in i.message.lower()]
        assert len(layout_issues) > 0

    def test_narrative_flow_no_title_slide(self, review_engine: ReviewEngine) -> None:
        definition = _make_definition([
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                title="Starts with Content",
                bullets=["No title slide at start"],
            ),
        ])
        result = review_engine.review_definition(definition)
        narrative_issues = [i for i in result.issues if "title slide" in i.message.lower()]
        assert len(narrative_issues) > 0

    def test_review_summary_generated(self, review_engine: ReviewEngine) -> None:
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Summary Test"),
        ])
        result = review_engine.review_definition(definition)
        assert result.summary
        assert "score" in result.summary.lower() or "/10" in result.summary


class TestSlidePlannerAgent:
    def test_basic_plan(self) -> None:
        planner = SlidePlannerAgent()
        result = planner.run(PlannerInput(
            topic="Test Topic",
            num_slides=8,
        ))
        assert len(result.slides) >= 3
        assert result.slides[0].slide_type == SlideType.TITLE
        assert result.metadata.title == "Test Topic"

    def test_plan_with_key_points(self) -> None:
        planner = SlidePlannerAgent()
        result = planner.run(PlannerInput(
            topic="Strategy Review",
            num_slides=10,
            key_points=["Market analysis", "Competitive landscape", "Recommendations"],
        ))
        assert len(result.slides) >= 5

    def test_plan_includes_agenda(self) -> None:
        planner = SlidePlannerAgent()
        result = planner.run(PlannerInput(
            topic="Agenda Test",
            include_agenda=True,
        ))
        agenda_slides = [s for s in result.slides if s.title and "agenda" in s.title.lower()]
        assert len(agenda_slides) >= 1

    def test_plan_with_deck_type(self) -> None:
        planner = SlidePlannerAgent()
        result = planner.run(PlannerInput(
            topic="Q4 Board Update",
            num_slides=10,
            deck_type="pitch_deck",
        ))
        types = [s.slide_type for s in result.slides]
        assert types[0] == SlideType.TITLE
        # Pitch deck should have diverse types, not just CONTENT
        unique_types = set(types)
        assert len(unique_types) >= 4

    def test_plan_content_aware_mapping(self) -> None:
        planner = SlidePlannerAgent()
        result = planner.run(PlannerInput(
            topic="Strategy Review",
            num_slides=8,
            key_points=["Revenue grew 47%", "Team updates", "Project timeline"],
        ))
        types = [s.slide_type for s in result.slides]
        # At least one non-CONTENT/SECTION type from smart mapping
        advanced = {SlideType.BIG_NUMBER, SlideType.TEAM, SlideType.TIMELINE,
                    SlideType.DASHBOARD, SlideType.PROCESS, SlideType.STEP_CARDS,
                    SlideType.ICON_GRID, SlideType.QUOTE, SlideType.FUNNEL, SlideType.SWOT}
        has_advanced = any(t in advanced for t in types)
        assert has_advanced, f"Expected content-aware types but got: {types}"

    def test_plan_variety_enforcement(self) -> None:
        planner = SlidePlannerAgent()
        result = planner.run(PlannerInput(
            topic="Test Variety",
            num_slides=15,
        ))
        types = [s.slide_type for s in result.slides]
        # No 3 consecutive identical non-SECTION types
        for i in range(2, len(types)):
            if types[i] not in (SlideType.SECTION, SlideType.TITLE):
                assert not (types[i] == types[i-1] == types[i-2]), (
                    f"Found 3+ consecutive {types[i]} at index {i}"
                )

    def test_plan_ends_with_closing(self) -> None:
        planner = SlidePlannerAgent()
        result = planner.run(PlannerInput(
            topic="Closing Test",
            num_slides=8,
            include_summary=True,
        ))
        assert result.slides[-1].slide_type == SlideType.CLOSING


class TestDesignAgent:
    def test_splits_overloaded_bullets(self) -> None:
        designer = DesignAgent()
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Deck Title"),
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                title="Overloaded",
                bullets=[f"Bullet {i}" for i in range(12)],
            ),
        ])
        result = designer.run(DesignerInput(definition=definition, brand_preset="executive"))
        assert result.definition.slides[1].slide_type == SlideType.TWO_COLUMN
        assert len(result.changes_made) > 0

    def test_promotes_single_bullet(self) -> None:
        designer = DesignAgent()
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Deck Title"),
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                title="Single Bullet",
                bullets=["This is the only point"],
            ),
        ])
        result = designer.run(DesignerInput(definition=definition, brand_preset="executive"))
        assert result.definition.slides[1].body == "This is the only point"
        assert len(result.definition.slides[1].bullets) == 0


    def test_promotes_content_to_big_number(self) -> None:
        designer = DesignAgent()
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Deck Title"),
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                title="Revenue Growth",
                bullets=["Revenue grew 47% year over year"],
            ),
        ])
        result = designer.run(DesignerInput(definition=definition, brand_preset="executive"))
        slide = result.definition.slides[1]
        assert slide.slide_type == SlideType.BIG_NUMBER
        assert "47%" in (slide.body or "")

    def test_promotes_content_to_icon_grid(self) -> None:
        designer = DesignAgent()
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Deck Title"),
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                title="Our Values",
                bullets=["Speed", "Trust", "Innovation", "Quality"],
            ),
        ])
        result = designer.run(DesignerInput(definition=definition, brand_preset="executive"))
        slide = result.definition.slides[1]
        assert slide.slide_type == SlideType.ICON_GRID
        assert len(slide.icons) == 4

    def test_visual_rhythm_enforcement(self) -> None:
        designer = DesignAgent()
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Test"),
            SlideDefinition(slide_type=SlideType.CONTENT, title="A", bullets=["One", "Two"]),
            SlideDefinition(slide_type=SlideType.CONTENT, title="B", bullets=["One", "Two"]),
            SlideDefinition(slide_type=SlideType.CONTENT, title="C", bullets=["One", "Two"]),
            SlideDefinition(slide_type=SlideType.CONTENT, title="D", bullets=["One", "Two"]),
            SlideDefinition(slide_type=SlideType.CONTENT, title="E", bullets=["One", "Two"]),
        ])
        result = designer.run(DesignerInput(definition=definition, brand_preset="executive"))
        types = [s.slide_type for s in result.definition.slides]
        assert SlideType.SECTION in types, f"Expected section break in {types}"

    def test_deck_structure_closing(self) -> None:
        designer = DesignAgent()
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Test"),
            SlideDefinition(slide_type=SlideType.CONTENT, title="Body", bullets=["Point"]),
        ])
        result = designer.run(DesignerInput(definition=definition, brand_preset="executive"))
        assert result.definition.slides[-1].slide_type == SlideType.CLOSING


class TestIterationAgent:
    def test_iteration_applies_fixes(self) -> None:
        definition = _make_definition([
            SlideDefinition(slide_type=SlideType.TITLE, title="Good Start"),
            SlideDefinition(
                slide_type=SlideType.CONTENT,
                title="Overloaded",
                bullets=[f"Point {i}" for i in range(10)],
            ),
        ])

        engine = ReviewEngine(brand=executive_default())
        review = engine.review_definition(definition)

        iterator = IterationAgent()
        result = iterator.run(IterationInput(
            definition=definition,
            review=review,
        ))
        # Should attempt to fix at least one issue
        assert isinstance(result.fixes_applied, list)
        assert isinstance(result.remaining_issues, list)
