"""SlidePlanner agent — converts a topic and context into a structured slide plan."""

from __future__ import annotations

import re
from typing import Optional

from pydantic import BaseModel, Field

from pptx_mcp.agents.archetypes import ARCHETYPES
from pptx_mcp.agents.base import BaseAgent
from pptx_mcp.agents.content_mapper import suggest_slide_type
from pptx_mcp.models.diagrams import (
    CycleStep,
    GanttTask,
    MatrixData,
    MatrixQuadrant,
    OrgNode,
    PyramidLayer,
    SpokeItem,
    VennCircle,
    VennData,
    WaterfallBar,
)
from pptx_mcp.models.slides import (
    AgendaItem,
    CardDefinition,
    ChartData,
    ChartDataSeries,
    ChartElement,
    ChartType,
    ColumnContent,
    FunnelStage,
    IconItem,
    KpiCard,
    PresentationDefinition,
    PresentationMetadata,
    ProcessStep,
    SlideDefinition,
    SlideType,
    SwotData,
    TableCell,
    TableElement,
    TeamMember,
)


class PlannerInput(BaseModel):
    """Input for the SlidePlanner agent."""

    topic: str = Field(description="Main topic or title of the presentation")
    audience: str = Field(default="executive", description="Target audience")
    num_slides: int = Field(default=10, ge=3, le=50, description="Target number of slides")
    key_points: list[str] = Field(default_factory=list, description="Key points to cover")
    context: str = Field(default="", description="Additional context or instructions")
    include_agenda: bool = Field(default=True, description="Include an agenda slide")
    include_summary: bool = Field(default=True, description="Include a summary/closing slide")
    author: Optional[str] = None
    company: Optional[str] = None
    deck_type: Optional[str] = Field(default=None, description="Deck archetype name (e.g. 'pitch_deck', 'board_deck')")


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

_NUMBER_RE = re.compile(r"(\d+[\d,.]*\s*[%$MBKmk]|\$[\d,.]+)")


def _extract_number(text: str) -> str | None:
    """Return the first number/percentage/currency token found in *text*."""
    m = _NUMBER_RE.search(text)
    return m.group(1).strip() if m else None


def _distribute_key_points(
    key_points: list[str],
    num_slides: int,
) -> list[list[str]]:
    """Evenly distribute *key_points* across *num_slides* content slots.

    Each slot receives 2-3 points when possible.  If there are fewer points
    than slots, later slots get topic-derived filler handled by the caller.
    """
    if not key_points or num_slides <= 0:
        return [[] for _ in range(max(num_slides, 0))]

    buckets: list[list[str]] = [[] for _ in range(num_slides)]
    for idx, point in enumerate(key_points):
        buckets[idx % num_slides].append(point)
    return buckets


class SlidePlannerAgent(BaseAgent[PlannerInput, PresentationDefinition]):
    """Generates a structured slide plan from a topic description."""

    @property
    def name(self) -> str:
        return "slide_planner"

    @property
    def description(self) -> str:
        return "Converts a topic and context into a structured slide plan."

    def run(self, input_data: PlannerInput) -> PresentationDefinition:
        self.logger.info("Planning presentation: '%s' (%d slides)", input_data.topic, input_data.num_slides)

        if input_data.deck_type and input_data.deck_type in ARCHETYPES:
            slides = self._plan_from_archetype(input_data)
        elif input_data.key_points:
            slides = self._plan_from_key_points_smart(input_data)
        else:
            slides = self._plan_default(input_data)

        # Enforce variety and structure
        slides = self._enforce_variety(slides)

        metadata = PresentationMetadata(
            title=input_data.topic,
            author=input_data.author,
            company=input_data.company,
            subject=input_data.topic,
        )

        return PresentationDefinition(metadata=metadata, slides=slides)

    # ------------------------------------------------------------------
    # Planning paths
    # ------------------------------------------------------------------

    def _plan_from_archetype(self, input_data: PlannerInput) -> list[SlideDefinition]:
        """Plan slides from a deck archetype."""
        archetype = ARCHETYPES[input_data.deck_type]  # type: ignore[index]
        target = max(archetype.min_slides, min(input_data.num_slides, archetype.max_slides))

        slots = list(archetype.slots)

        # Scale: if target > slots, repeat optional/content slots
        slides: list[SlideDefinition] = []
        for slot in slots:
            title = slot.title_template
            slide = self._create_skeleton_slide(slot.slide_type, title, len(slides), input_data)
            slides.append(slide)

        # Fill remaining budget with CONTENT slides
        while len(slides) < target:
            idx = len(slides)
            slides.insert(-1, self._create_skeleton_slide(
                SlideType.CONTENT,
                f"Detail Slide {idx}",
                idx,
                input_data,
            ))

        # Trim if over budget (keep first, last, and evenly spaced)
        if len(slides) > target:
            first = slides[0]
            last = slides[-1]
            middle = slides[1:-1]
            step = max(1, len(middle) // (target - 2))
            kept = middle[::step][:target - 2]
            slides = [first] + kept + [last]

        return slides[:target]

    def _plan_from_key_points_smart(self, input_data: PlannerInput) -> list[SlideDefinition]:
        """Plan content slides using content-aware type selection."""
        slides: list[SlideDefinition] = []

        # Title
        slides.append(SlideDefinition(
            slide_type=SlideType.TITLE,
            title=input_data.topic,
            subtitle=self._generate_subtitle(input_data),
            speaker_notes="Introduce the topic and set expectations.",
        ))

        # Agenda
        if input_data.include_agenda:
            slides.append(SlideDefinition(
                slide_type=SlideType.AGENDA,
                title="Agenda",
                agenda_items=[AgendaItem(title=kp) for kp in input_data.key_points[:8]],
                speaker_notes="Walk through what we'll cover today.",
            ))

        # Content budget
        content_budget = input_data.num_slides - len(slides)
        if input_data.include_summary:
            content_budget -= 1

        for i, point in enumerate(input_data.key_points[:content_budget]):
            if i > 0 and i % 4 == 0:
                slides.append(SlideDefinition(
                    slide_type=SlideType.SECTION,
                    title=point,
                    speaker_notes=f"Transition to: {point}",
                ))
                continue

            stype = suggest_slide_type(point, input_data.context)
            slide = self._create_skeleton_slide(stype, point, i, input_data)
            slides.append(slide)

        # Fill remaining
        while len(slides) < input_data.num_slides - (1 if input_data.include_summary else 0):
            slides.append(self._create_skeleton_slide(
                SlideType.CONTENT,
                f"Additional Detail {len(slides)}",
                len(slides),
                input_data,
            ))

        # Closing
        if input_data.include_summary:
            slides.append(SlideDefinition(
                slide_type=SlideType.CLOSING,
                title="Key Takeaways",
                subtitle=self._closing_message(input_data),
                bullets=self._generate_takeaways(input_data),
                speaker_notes="Summarize key points and call to action.",
            ))

        return slides

    def _plan_default(self, input_data: PlannerInput) -> list[SlideDefinition]:
        """Plan slides with a varied default structure."""
        slides: list[SlideDefinition] = []

        # Title
        slides.append(SlideDefinition(
            slide_type=SlideType.TITLE,
            title=input_data.topic,
            subtitle=self._generate_subtitle(input_data),
            speaker_notes="Introduce the topic and set expectations.",
        ))

        # Agenda
        if input_data.include_agenda:
            agenda_items = self._generate_agenda(input_data)
            slides.append(SlideDefinition(
                slide_type=SlideType.AGENDA,
                title="Agenda",
                agenda_items=[AgendaItem(title=item) for item in agenda_items],
                speaker_notes="Walk through what we'll cover today.",
            ))

        content_budget = input_data.num_slides - len(slides)
        if input_data.include_summary:
            content_budget -= 1

        structure = self._get_default_structure(content_budget)
        for i, (stype, title_template) in enumerate(structure):
            title = title_template.format(topic=input_data.topic)
            slide = self._create_skeleton_slide(stype, title, i, input_data)
            slides.append(slide)

        # Closing
        if input_data.include_summary:
            slides.append(SlideDefinition(
                slide_type=SlideType.CLOSING,
                title="Key Takeaways",
                subtitle=self._closing_message(input_data),
                bullets=self._generate_takeaways(input_data),
                speaker_notes="Summarize key points and call to action.",
            ))

        return slides

    # ------------------------------------------------------------------
    # Enforcement / structure helpers  (unchanged logic)
    # ------------------------------------------------------------------

    def _enforce_variety(self, slides: list[SlideDefinition]) -> list[SlideDefinition]:
        """Ensure no 3+ consecutive identical non-SECTION types."""
        if len(slides) < 3:
            return slides

        result = list(slides)
        i = 2
        while i < len(result):
            if (
                result[i].slide_type == result[i - 1].slide_type == result[i - 2].slide_type
                and result[i].slide_type not in (SlideType.SECTION, SlideType.TITLE)
            ):
                # Insert a section break before the third consecutive
                result.insert(i, SlideDefinition(
                    slide_type=SlideType.SECTION,
                    title="[Section Break]",
                    speaker_notes="Section break for visual variety.",
                ))
                i += 2  # skip past the inserted section
            else:
                i += 1

        return result

    # ------------------------------------------------------------------
    # Subtitle / agenda / structure helpers
    # ------------------------------------------------------------------

    def _generate_subtitle(self, input_data: PlannerInput) -> str:
        parts: list[str] = []
        if input_data.company:
            parts.append(input_data.company)
        if input_data.audience == "executive":
            parts.append("Executive Overview")
        elif input_data.audience == "technical":
            parts.append("Technical Deep Dive")
        elif input_data.audience:
            parts.append(f"Prepared for {input_data.audience}")
        else:
            parts.append("Overview")
        if input_data.key_points:
            parts.append(f"{len(input_data.key_points)} key topics")
        return " | ".join(parts) if parts else "Overview"

    def _closing_message(self, input_data: PlannerInput) -> str:
        if input_data.company:
            return f"Thank you — {input_data.company}"
        return f"Thank you for your time"

    def _generate_agenda(self, input_data: PlannerInput) -> list[str]:
        if input_data.key_points:
            return input_data.key_points[:6]
        return [
            "Background & Context",
            "Current State Analysis",
            "Proposed Approach",
            "Key Considerations",
            "Next Steps",
        ]

    def _get_default_structure(self, budget: int) -> list[tuple[SlideType, str]]:
        full_structure: list[tuple[SlideType, str]] = [
            (SlideType.SECTION, "Background"),
            (SlideType.CONTENT, "Current State"),
            (SlideType.TWO_COLUMN, "Analysis"),
            (SlideType.BIG_NUMBER, "Key Metric"),
            (SlideType.SECTION, "Approach"),
            (SlideType.CONTENT, "Proposed Solution"),
            (SlideType.PROS_CONS, "Trade-offs"),
            (SlideType.CHART, "Impact & Metrics"),
            (SlideType.DASHBOARD, "Performance Overview"),
            (SlideType.SECTION, "Path Forward"),
            (SlideType.PROCESS, "Implementation Plan"),
            (SlideType.TIMELINE, "Timeline & Resources"),
            (SlideType.COMPARISON, "Options Comparison"),
            (SlideType.QUOTE, "Expert Perspective"),
        ]

        if budget <= len(full_structure):
            step = max(1, len(full_structure) // budget)
            return full_structure[::step][:budget]

        result = list(full_structure)
        while len(result) < budget:
            result.append((SlideType.CONTENT, f"Detail Slide {len(result) + 1}"))
        return result[:budget]

    # ------------------------------------------------------------------
    # Takeaways
    # ------------------------------------------------------------------

    def _generate_takeaways(self, input_data: PlannerInput) -> list[str]:
        topic = input_data.topic
        if input_data.key_points:
            takeaways = [
                f"{kp} — a critical factor for success"
                if i == 0
                else kp
                for i, kp in enumerate(input_data.key_points[:3])
            ]
            takeaways.append(f"Next step: align on {topic} priorities")
            return takeaways
        return [
            f"Strong foundation established for {topic}",
            f"Key opportunities identified for {input_data.audience} stakeholders",
            f"Clear path forward on {topic}",
            f"Align on next steps and owners",
        ]

    # ------------------------------------------------------------------
    # Content helpers for _create_skeleton_slide
    # ------------------------------------------------------------------

    def _points_for_slide(self, input_data: PlannerInput, index: int, count: int = 3) -> list[str]:
        """Return *count* bullet points for a content slide at *index*.

        Round-robins through key_points, falling back to topic-derived text.
        """
        topic = input_data.topic
        kps = input_data.key_points
        if kps:
            start = (index * count) % len(kps) if len(kps) > count else 0
            selected = (kps + kps)[start:start + count]  # wrap-around slice
            return selected[:count]
        audience = input_data.audience or "stakeholders"
        return [
            f"Overview of {topic}",
            f"Key considerations for {audience}",
            f"Implications and next steps for {topic}",
        ][:count]

    def _split_key_points_two(self, input_data: PlannerInput) -> tuple[list[str], list[str]]:
        """Split key_points into two roughly equal groups."""
        kps = input_data.key_points
        topic = input_data.topic
        if len(kps) >= 2:
            mid = len(kps) // 2
            return kps[:mid], kps[mid:]
        # Fallback: generate from topic
        return (
            [f"Current {topic} capabilities", f"Existing {topic} strengths"],
            [f"Future {topic} opportunities", f"Planned {topic} improvements"],
        )

    # ------------------------------------------------------------------
    # Skeleton slide factory
    # ------------------------------------------------------------------

    def _create_skeleton_slide(
        self,
        slide_type: SlideType,
        title: str,
        index: int,
        input_data: PlannerInput,
    ) -> SlideDefinition:
        """Create a slide with real content derived from *input_data*."""
        topic = input_data.topic
        company = input_data.company or topic
        audience = input_data.audience or "stakeholders"
        kps = input_data.key_points

        # ---- SECTION ----
        if slide_type == SlideType.SECTION:
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                speaker_notes=f"Transition: {title}",
            )

        # ---- TWO_COLUMN ----
        if slide_type == SlideType.TWO_COLUMN:
            col_a, col_b = self._split_key_points_two(input_data)
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                columns=[
                    ColumnContent(title="Current State", bullets=col_a[:3]),
                    ColumnContent(title="Future State", bullets=col_b[:3]),
                ],
                speaker_notes=f"Discuss {title} from two perspectives.",
            )

        # ---- PROS_CONS ----
        if slide_type == SlideType.PROS_CONS:
            pros = [
                f"Aligns with {topic} objectives",
                f"Builds on existing {audience} relationships",
                f"Accelerates {topic} outcomes",
            ]
            cons = [
                f"Requires investment in {topic}",
                f"Timeline pressure on {topic} delivery",
                f"Resource constraints for {audience}",
            ]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                pros=pros,
                cons=cons,
                speaker_notes=f"Weigh the pros and cons of {title}.",
            )

        # ---- COMPARISON ----
        if slide_type == SlideType.COMPARISON:
            if len(kps) >= 3:
                cols = [
                    ColumnContent(title=kps[i], bullets=[f"Details for {kps[i]}"])
                    for i in range(min(3, len(kps)))
                ]
            else:
                cols = [
                    ColumnContent(title="Option A", bullets=[f"Optimized for {topic} speed"]),
                    ColumnContent(title="Option B", bullets=[f"Optimized for {topic} cost"]),
                    ColumnContent(title="Option C", bullets=[f"Balanced {topic} approach"]),
                ]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                columns=cols,
                speaker_notes=f"Compare options for {title}.",
            )

        # ---- QUOTE ----
        if slide_type == SlideType.QUOTE:
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                body=f"The future of {topic} depends on our ability to execute with clarity and conviction",
                quote_attribution="Industry Expert",
                quote_attribution_role=f"{topic} Thought Leader",
                speaker_notes=f"Share the context behind this quote and how it relates to {topic}.",
            )

        # ---- BIG_NUMBER ----
        if slide_type == SlideType.BIG_NUMBER:
            # Try to pull a real number from key_points
            extracted: str | None = None
            source_label = topic
            for kp in kps:
                extracted = _extract_number(kp)
                if extracted:
                    source_label = kp
                    break
            value = extracted or "100%"
            label = f"{topic} achievement" if not extracted else source_label
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                body=value,
                subtitle=label,
                bullets=[f"Demonstrates strong momentum in {topic}"],
                speaker_notes=f"Highlight the significance of this metric for {topic}.",
            )

        # ---- PROCESS ----
        if slide_type == SlideType.PROCESS:
            step_titles = (
                kps[:4] if kps
                else ["Assess", "Plan", "Execute", "Review"]
            )
            steps = []
            for si, st in enumerate(step_titles):
                if si == 0:
                    status = "completed"
                elif si == 1:
                    status = "active"
                else:
                    status = "future"
                steps.append(ProcessStep(title=st, description=f"{st} — {topic}", status=status))
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                steps=steps,
                active_step=1,
                speaker_notes=f"Walk through each step of the {topic} process and current progress.",
            )

        # ---- TIMELINE ----
        if slide_type == SlideType.TIMELINE:
            step_titles = (
                kps[:4] if kps
                else ["Discovery", "Planning", "Execution", "Delivery"]
            )
            steps = []
            quarter_labels = ["Q1", "Q2", "Q3", "Q4"]
            for si, st in enumerate(step_titles):
                ql = quarter_labels[si % len(quarter_labels)]
                if si == 0:
                    status = "completed"
                elif si == 1:
                    status = "active"
                else:
                    status = "future"
                steps.append(ProcessStep(title=st, date=ql, status=status))
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                steps=steps,
                speaker_notes=f"Review the timeline milestones and current status for {topic}.",
            )

        # ---- STEP_CARDS ----
        if slide_type == SlideType.STEP_CARDS:
            step_titles = (
                kps[:4] if kps
                else ["Assess", "Plan", "Execute", "Review"]
            )
            steps = [
                ProcessStep(title=st, description=f"{st} for {topic}")
                for st in step_titles
            ]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                steps=steps,
                speaker_notes=f"Explain each step and how they connect in the {topic} workflow.",
            )

        # ---- DASHBOARD ----
        if slide_type == SlideType.DASHBOARD:
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                kpis=[
                    KpiCard(value="87%", label="Progress", change="+12%", trend="up"),
                    KpiCard(value="12/15", label="Completion Rate", change="+3", trend="up"),
                    KpiCard(value="4.2/5", label="Team Capacity", trend="neutral"),
                    KpiCard(value="On Track", label="Timeline Status", trend="up"),
                ],
                speaker_notes=f"Review each KPI and highlight trends relevant to {topic}.",
            )

        # ---- ICON_GRID ----
        if slide_type == SlideType.ICON_GRID:
            items = kps[:6] if kps else [
                f"{topic} strategy",
                f"{topic} execution",
                f"{topic} measurement",
                f"{topic} optimization",
            ]
            icons_list = ["\u2605", "\u25CF", "\u25B6", "\u2713", "\u2666", "\u25A0"]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                icons=[
                    IconItem(
                        icon=icons_list[i % len(icons_list)],
                        title=item,
                        description=f"Key aspect of {topic}",
                    )
                    for i, item in enumerate(items)
                ],
                speaker_notes=f"Briefly describe each icon and its relevance to {topic}.",
            )

        # ---- CARD_GRID ----
        if slide_type == SlideType.CARD_GRID:
            items = kps[:6] if kps else [
                f"{topic} strategy", f"{topic} execution",
                f"{topic} measurement", f"{topic} optimization",
            ]
            icons_list = ["\u2605", "\u25CF", "\u25B6", "\u2713", "\u2666", "\u25A0"]
            return SlideDefinition(
                slide_type=slide_type, title=title,
                cards=[CardDefinition(
                    title=item, body=f"Key aspect of {topic}",
                    icon=icons_list[i % len(icons_list)],
                ) for i, item in enumerate(items)],
                speaker_notes=f"Walk through each {topic} capability.",
            )

        # ---- IMAGE_TEXT ----
        if slide_type == SlideType.IMAGE_TEXT:
            bullets = self._points_for_slide(input_data, index, count=2)
            body = bullets[0] if bullets else f"Overview of {topic}"
            remaining = bullets[1:] if len(bullets) > 1 else [f"Key insight for {audience}"]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                body=body,
                bullets=remaining,
                image_position="left",
                speaker_notes=f"Describe the visual and connect it to {topic}.",
            )

        # ---- TEAM ----
        if slide_type == SlideType.TEAM:
            org = company
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                team_members=[
                    TeamMember(name="Project Lead", role=f"Leads {topic} at {org}"),
                    TeamMember(name="Technical Lead", role=f"Drives {topic} engineering at {org}"),
                    TeamMember(name="Program Manager", role=f"Coordinates {topic} delivery at {org}"),
                ],
                speaker_notes=f"Introduce each team member and their role in {topic}.",
            )

        # ---- CLOSING ----
        if slide_type == SlideType.CLOSING:
            subtitle = self._closing_message(input_data)
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                subtitle=subtitle,
                speaker_notes="Summarize and close.",
            )

        # ---- SWOT ----
        if slide_type == SlideType.SWOT:
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                swot=SwotData(
                    strengths=[f"Strong foundation in {topic}", f"Experienced {audience} team"],
                    weaknesses=[f"Limited resources for {topic}", f"Competing {topic} priorities"],
                    opportunities=[f"Growing demand for {topic}", f"New {topic} partnerships"],
                    threats=[f"Market shifts affecting {topic}", f"Talent competition in {topic}"],
                ),
                speaker_notes=f"Walk through each quadrant of the SWOT analysis for {topic}.",
            )

        # ---- FUNNEL ----
        if slide_type == SlideType.FUNNEL:
            stages = (
                kps[:5] if kps
                else ["Awareness", "Interest", "Evaluation", "Decision"]
            )
            descending_values = ["1,000", "600", "250", "120", "50"]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                funnel_stages=[
                    FunnelStage(
                        label=s,
                        value=descending_values[i] if i < len(descending_values) else None,
                    )
                    for i, s in enumerate(stages)
                ],
                speaker_notes=f"Explain conversion rates at each stage of the {topic} funnel.",
            )

        # ---- AGENDA ----
        if slide_type == SlideType.AGENDA:
            items = kps[:8] if kps else self._generate_agenda(input_data)
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                agenda_items=[AgendaItem(title=item) for item in items],
                speaker_notes="Walk through the agenda and set expectations for the session.",
            )

        # ---- CHART ----
        if slide_type == SlideType.CHART:
            points = self._points_for_slide(input_data, index, count=4)
            categories = [p.split(":")[0].strip()[:30] if ":" in p else p[:30] for p in points]
            values = []
            for p in points:
                nums = re.findall(r"[\d.]+", p)
                values.append(float(nums[0]) if nums else float(hash(p) % 90 + 10))
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                elements=[
                    ChartElement(
                        chart_type=ChartType.BAR,
                        data=ChartData(
                            categories=categories,
                            series=[ChartDataSeries(name=topic[:30], values=values)],
                        ),
                    ),
                ],
                speaker_notes=f"Walk through each data point and highlight key trends in {topic}.",
            )

        # ---- TABLE ----
        if slide_type == SlideType.TABLE:
            points = self._points_for_slide(input_data, index, count=4)
            header = [TableCell(text="Item"), TableCell(text="Details"), TableCell(text="Status")]
            rows = [header]
            for p in points:
                parts = p.split(":", 1)
                item_text = parts[0].strip()
                detail = parts[1].strip() if len(parts) > 1 else f"Details for {item_text}"
                rows.append([
                    TableCell(text=item_text),
                    TableCell(text=detail),
                    TableCell(text="Active"),
                ])
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                elements=[TableElement(rows=rows)],
                speaker_notes=f"Review each row and discuss the status of {topic} items.",
            )

        # ---- ORG_CHART ----
        if slide_type == SlideType.ORG_CHART:
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                org_chart=OrgNode(
                    id="lead",
                    name=f"{topic} Lead",
                    role="Program Lead",
                    children=[
                        OrgNode(id="eng", name="Engineering", role="Technical"),
                        OrgNode(id="ops", name="Operations", role="Delivery"),
                        OrgNode(id="biz", name="Business", role="Strategy"),
                    ],
                ),
                speaker_notes=f"Walk through the {topic} organizational structure.",
            )

        # ---- MATRIX ----
        if slide_type == SlideType.MATRIX:
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                matrix=MatrixData(
                    x_axis_label="Impact",
                    y_axis_label="Effort",
                    top_left=MatrixQuadrant(label="Quick Wins", items=[kps[0] if kps else f"{topic} quick win"]),
                    top_right=MatrixQuadrant(label="Major Projects", items=[kps[1] if len(kps) > 1 else f"{topic} initiative"]),
                    bottom_left=MatrixQuadrant(label="Fill-Ins", items=[kps[2] if len(kps) > 2 else f"{topic} maintenance"]),
                    bottom_right=MatrixQuadrant(label="Thankless Tasks", items=[kps[3] if len(kps) > 3 else f"{topic} overhead"]),
                ),
                speaker_notes=f"Prioritize {topic} initiatives using the effort/impact matrix.",
            )

        # ---- PYRAMID ----
        if slide_type == SlideType.PYRAMID:
            layer_labels = kps[:4] if kps else ["Strategy", "Tactics", "Operations", "Execution"]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                pyramid_layers=[
                    PyramidLayer(label=lbl, description=f"{lbl} layer of {topic}")
                    for lbl in layer_labels
                ],
                speaker_notes=f"Explain the hierarchical layers of {topic}.",
            )

        # ---- VENN ----
        if slide_type == SlideType.VENN:
            labels = kps[:3] if len(kps) >= 2 else [f"{topic} A", f"{topic} B"]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                venn=VennData(
                    circles=[VennCircle(label=lbl) for lbl in labels[:3]],
                    intersection_label=f"Core {topic}",
                ),
                speaker_notes=f"Show the overlap and synergies across {topic} areas.",
            )

        # ---- WATERFALL ----
        if slide_type == SlideType.WATERFALL:
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                waterfall_bars=[
                    WaterfallBar(label="Baseline", value=100, bar_type="total"),
                    WaterfallBar(label="Growth", value=30, bar_type="increase"),
                    WaterfallBar(label="Efficiency", value=15, bar_type="increase"),
                    WaterfallBar(label="Churn", value=-20, bar_type="decrease"),
                    WaterfallBar(label="Costs", value=-10, bar_type="decrease"),
                    WaterfallBar(label="Net", value=115, bar_type="total"),
                ],
                speaker_notes=f"Walk through each component contributing to the {topic} outcome.",
            )

        # ---- HUB_SPOKE ----
        if slide_type == SlideType.HUB_SPOKE:
            spoke_labels = kps[:6] if kps else [
                f"{topic} Strategy", f"{topic} Execution",
                f"{topic} Measurement", f"{topic} Optimization",
            ]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                hub_spoke_center=topic[:30],
                spokes=[SpokeItem(label=lbl) for lbl in spoke_labels],
                speaker_notes=f"Explain how each spoke connects to the central {topic} hub.",
            )

        # ---- CYCLE ----
        if slide_type == SlideType.CYCLE:
            step_labels = kps[:6] if kps else ["Plan", "Build", "Test", "Deploy"]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                cycle_steps=[CycleStep(label=lbl) for lbl in step_labels],
                speaker_notes=f"Describe the continuous {topic} cycle and how each step feeds the next.",
            )

        # ---- GANTT ----
        if slide_type == SlideType.GANTT:
            task_labels = kps[:5] if kps else ["Discovery", "Design", "Build", "Test", "Launch"]
            return SlideDefinition(
                slide_type=slide_type,
                title=title,
                gantt_tasks=[
                    GanttTask(
                        label=lbl,
                        start=round(i * 0.18, 2),
                        duration=round(0.25 + (i % 2) * 0.1, 2),
                        progress=max(0.0, 1.0 - i * 0.25),
                    )
                    for i, lbl in enumerate(task_labels)
                ],
                gantt_phases=["Phase 1", "Phase 2", "Phase 3"],
                speaker_notes=f"Review the {topic} project timeline and task progress.",
            )

        # ---- Default: CONTENT / DIAGRAM / BLANK / TITLE ----
        bullets = self._points_for_slide(input_data, index, count=3)
        return SlideDefinition(
            slide_type=slide_type,
            title=title,
            bullets=bullets,
            speaker_notes=f"Cover the key aspects of {title}.",
        )
