"""Phase 2 tests — card grid, callouts, badges, and spec parser extensions."""

from __future__ import annotations

from pathlib import Path

import pytest
from pptx import Presentation

from pptx_mcp.brand.defaults import PRESETS
from pptx_mcp.engine.generator import DeckGenerator
from pptx_mcp.engine.spec_parser import extract_spec, parse_spec
from pptx_mcp.models.slides import (
    Badge,
    CalloutBox,
    CardDefinition,
    KpiCard,
    PresentationDefinition,
    PresentationMetadata,
    SlideDefinition,
    SlideType,
)


# ---------------------------------------------------------------------------
# Model validation
# ---------------------------------------------------------------------------


class TestBadgeModel:
    def test_valid_badge(self) -> None:
        b = Badge(text="NEW")
        assert b.text == "NEW"
        assert b.color is None

    def test_badge_with_color(self) -> None:
        b = Badge(text="HOT", color="#FF0000")
        assert b.color == "#FF0000"

    def test_badge_max_length(self) -> None:
        with pytest.raises(Exception):
            Badge(text="A" * 31)


class TestCardDefinitionModel:
    def test_minimal_card(self) -> None:
        c = CardDefinition(title="Test")
        assert c.title == "Test"
        assert c.body is None
        assert c.bullets == []
        assert c.badge is None

    def test_full_card(self) -> None:
        c = CardDefinition(
            title="Strategy",
            body="Advisory services",
            icon="\U0001F3AF",
            bullets=["Market analysis", "Growth planning"],
            badge=Badge(text="NEW"),
            accent_color="#FF5722",
        )
        assert len(c.bullets) == 2
        assert c.badge.text == "NEW"
        assert c.icon == "\U0001F3AF"


class TestCalloutBoxModel:
    def test_default_variant(self) -> None:
        c = CalloutBox(body="Note text")
        assert c.variant == "info"
        assert c.title is None

    def test_warning_variant(self) -> None:
        c = CalloutBox(variant="warning", title="Caution", body="Be careful")
        assert c.variant == "warning"

    def test_success_variant(self) -> None:
        c = CalloutBox(variant="success", body="All good")
        assert c.variant == "success"


class TestKpiCardBadge:
    def test_kpi_with_badge(self) -> None:
        k = KpiCard(value="99%", label="Uptime", badge=Badge(text="SLA"))
        assert k.badge.text == "SLA"

    def test_kpi_without_badge(self) -> None:
        k = KpiCard(value="42", label="Score")
        assert k.badge is None


# ---------------------------------------------------------------------------
# Rendering
# ---------------------------------------------------------------------------


def _generate_single_slide(slide_def: SlideDefinition, tmp_path: Path, preset: str = "executive") -> Path:
    """Helper: render one slide and return the output path."""
    brand = PRESETS[preset].model_copy(deep=True)
    gen = DeckGenerator(brand=brand)
    defn = PresentationDefinition(
        metadata=PresentationMetadata(title="Phase2 Test"),
        slides=[slide_def],
    )
    out = tmp_path / "out.pptx"
    return gen.generate(defn, out)


class TestCardGridRendering:
    def test_render_2_cards(self, tmp_path: Path) -> None:
        sd = SlideDefinition(
            slide_type=SlideType.CARD_GRID,
            title="Services",
            cards=[
                CardDefinition(title="A", body="Desc A"),
                CardDefinition(title="B", body="Desc B"),
            ],
        )
        path = _generate_single_slide(sd, tmp_path)
        assert path.exists()
        prs = Presentation(str(path))
        assert len(prs.slides) == 1

    def test_render_cards_with_badges_and_icons(self, tmp_path: Path) -> None:
        sd = SlideDefinition(
            slide_type=SlideType.CARD_GRID,
            title="Capabilities",
            cards=[
                CardDefinition(title="AI", icon="\U0001F916", badge=Badge(text="NEW"), bullets=["ML", "NLP"]),
                CardDefinition(title="Cloud", icon="\u2601\uFE0F", bullets=["AWS", "GCP"]),
                CardDefinition(title="Data", icon="\U0001F4CA", body="Analytics platform"),
            ],
        )
        path = _generate_single_slide(sd, tmp_path)
        prs = Presentation(str(path))
        assert len(prs.slides) == 1
        # Many shapes: cards + accent bars + icons + titles + badges + bullets
        assert len(prs.slides[0].shapes) > 6

    def test_render_empty_cards_list(self, tmp_path: Path) -> None:
        sd = SlideDefinition(slide_type=SlideType.CARD_GRID, title="Empty")
        path = _generate_single_slide(sd, tmp_path)
        assert path.exists()

    def test_render_max_cards(self, tmp_path: Path) -> None:
        cards = [CardDefinition(title=f"Card {i}") for i in range(12)]
        sd = SlideDefinition(slide_type=SlideType.CARD_GRID, title="Max", cards=cards)
        path = _generate_single_slide(sd, tmp_path)
        assert path.exists()


class TestCalloutRendering:
    def test_callout_on_content_slide(self, tmp_path: Path) -> None:
        sd = SlideDefinition(
            slide_type=SlideType.CONTENT,
            title="Report",
            bullets=["Point 1"],
            callout=CalloutBox(variant="info", body="This is an informational note."),
        )
        path = _generate_single_slide(sd, tmp_path)
        assert path.exists()

    def test_callout_warning_with_title(self, tmp_path: Path) -> None:
        sd = SlideDefinition(
            slide_type=SlideType.CONTENT,
            title="Risks",
            callout=CalloutBox(variant="warning", title="Important", body="Review required."),
        )
        path = _generate_single_slide(sd, tmp_path)
        prs = Presentation(str(path))
        assert len(prs.slides) == 1

    def test_callout_on_card_grid(self, tmp_path: Path) -> None:
        sd = SlideDefinition(
            slide_type=SlideType.CARD_GRID,
            title="With Callout",
            cards=[CardDefinition(title="A"), CardDefinition(title="B")],
            callout=CalloutBox(variant="success", body="All checks passed."),
        )
        path = _generate_single_slide(sd, tmp_path)
        assert path.exists()


class TestDashboardBadge:
    def test_kpi_badge_renders(self, tmp_path: Path) -> None:
        sd = SlideDefinition(
            slide_type=SlideType.DASHBOARD,
            title="Metrics",
            kpis=[
                KpiCard(value="99.9%", label="Uptime", badge=Badge(text="SLA")),
                KpiCard(value="42ms", label="Latency"),
            ],
        )
        path = _generate_single_slide(sd, tmp_path)
        assert path.exists()


# ---------------------------------------------------------------------------
# Spec parser
# ---------------------------------------------------------------------------


class TestSpecParserCards:
    def test_card_blocks_detected(self) -> None:
        md = """# Test Deck

## Slide 1: Our Services

### Strategy
- Market analysis
- Growth planning

### Implementation
- Agile delivery
- Change management

### Analytics
- Dashboards
- Predictive models
"""
        defn, _ = parse_spec(md)
        assert len(defn.slides) == 1
        slide = defn.slides[0]
        assert slide.slide_type == SlideType.CARD_GRID
        assert len(slide.cards) == 3
        assert slide.cards[0].title == "Strategy"
        assert "Market analysis" in slide.cards[0].bullets

    def test_callout_detected(self) -> None:
        md = """# Test Deck

## Slide 1: Key Findings

- Finding A
- Finding B

> **Warning:** Data is preliminary and subject to change.
"""
        defn, _ = parse_spec(md)
        slide = defn.slides[0]
        assert slide.callout is not None
        assert slide.callout.variant == "warning"
        assert "preliminary" in slide.callout.body

    def test_info_callout(self) -> None:
        md = """# Test

## Slide 1: Overview

Some text.

> **Note:** This is informational.
"""
        defn, _ = parse_spec(md)
        slide = defn.slides[0]
        assert slide.callout is not None
        assert slide.callout.variant == "info"

    def test_card_grid_keyword(self) -> None:
        spec = extract_spec("""# Deck

## Slide 1: Card Grid Overview

Some intro text.
""")
        from pptx_mcp.engine.spec_parser import _infer_slide_type
        slide_type = _infer_slide_type(spec.slides[0].title, spec.slides[0].content_blocks)
        assert slide_type == SlideType.CARD_GRID
