"""Slide description generator — produces natural-language summaries of presentations."""

from __future__ import annotations

from pptx_mcp.models.slides import SlideDefinition, SlideType


def describe_slide(slide: SlideDefinition, index: int) -> str:
    """Generate a one-sentence natural-language description of a slide.

    Args:
        slide: The slide definition to describe.
        index: 0-based slide index.

    Returns:
        A human-readable description string.
    """
    title = slide.title or "(untitled)"
    prefix = f"Slide {index + 1}"

    type_desc = _TYPE_DESCRIPTIONS.get(slide.slide_type, "content slide")

    # Build type-specific detail
    detail = _build_detail(slide)

    if detail:
        return f"{prefix}: {type_desc} — \"{title}\" {detail}"
    return f"{prefix}: {type_desc} — \"{title}\""


def describe_presentation(slides: list[SlideDefinition]) -> dict:
    """Produce a structured analysis of a presentation.

    Args:
        slides: List of slide definitions.

    Returns:
        Dict with slide_descriptions, content_inventory, type_distribution,
        narrative_assessment, and recommendations.
    """
    slide_descriptions = [describe_slide(s, i) for i, s in enumerate(slides)]

    # Content inventory
    word_counts = []
    visual_count = 0
    text_count = 0
    _VISUAL_TYPES = {
        SlideType.CHART, SlideType.DIAGRAM, SlideType.DASHBOARD,
        SlideType.PROCESS, SlideType.TIMELINE, SlideType.FUNNEL,
        SlideType.ICON_GRID, SlideType.CARD_GRID, SlideType.IMAGE_TEXT,
        SlideType.ORG_CHART, SlideType.MATRIX, SlideType.PYRAMID,
        SlideType.VENN, SlideType.WATERFALL, SlideType.HUB_SPOKE,
        SlideType.CYCLE, SlideType.GANTT,
    }
    for s in slides:
        wc = len(_gather_slide_text(s).split())
        word_counts.append(wc)
        if s.slide_type in _VISUAL_TYPES:
            visual_count += 1
        else:
            text_count += 1

    total_words = sum(word_counts)
    content_inventory = {
        "total_words": total_words,
        "words_per_slide": word_counts,
        "visual_slide_count": visual_count,
        "text_slide_count": text_count,
    }

    # Type distribution
    type_dist: dict[str, int] = {}
    for s in slides:
        key = s.slide_type.value
        type_dist[key] = type_dist.get(key, 0) + 1

    # Narrative assessment
    opens_with_title = bool(slides and slides[0].slide_type == SlideType.TITLE)
    closes_properly = bool(
        slides
        and slides[-1].slide_type in (SlideType.CLOSING, SlideType.TITLE)
    )
    has_section_breaks = any(s.slide_type == SlideType.SECTION for s in slides)

    unique_types = len(set(s.slide_type for s in slides))
    variety_score = min(1.0, unique_types / max(len(slides), 1) * 2)

    notes: list[str] = []
    if not opens_with_title:
        notes.append("Deck does not open with a title slide.")
    if not closes_properly:
        notes.append("Deck does not end with a closing or title slide.")
    if not has_section_breaks and len(slides) > 5:
        notes.append("No section breaks found — consider adding dividers for structure.")
    if variety_score < 0.3:
        notes.append("Low layout variety — mix in more visual slide types.")

    narrative_assessment = {
        "opens_with_title": opens_with_title,
        "closes_properly": closes_properly,
        "has_section_breaks": has_section_breaks,
        "variety_score": round(variety_score, 2),
        "notes": notes,
    }

    # Recommendations
    recommendations: list[str] = []
    if total_words > 0 and len(slides) > 0:
        avg_words = total_words / len(slides)
        if avg_words > 80:
            recommendations.append(
                f"Average {avg_words:.0f} words/slide — consider reducing text density."
            )
    if visual_count == 0 and len(slides) > 3:
        recommendations.append("No visual slides detected — add charts, diagrams, or dashboards.")
    if not opens_with_title:
        recommendations.append("Start with a title slide to set context.")
    if not closes_properly:
        recommendations.append("End with a closing slide for a strong finish.")
    if len(slides) > 20:
        recommendations.append(
            f"Deck has {len(slides)} slides — consider trimming to under 20 for executive audiences."
        )
    # Check for consecutive same-type slides
    for i in range(2, len(slides)):
        if (slides[i].slide_type == slides[i-1].slide_type == slides[i-2].slide_type
                and slides[i].slide_type not in (SlideType.SECTION, SlideType.TITLE)):
            recommendations.append("Three or more consecutive slides of the same type detected — add variety.")
            break

    return {
        "slide_descriptions": slide_descriptions,
        "content_inventory": content_inventory,
        "type_distribution": type_dist,
        "narrative_assessment": narrative_assessment,
        "recommendations": recommendations,
    }


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------

_TYPE_DESCRIPTIONS: dict[SlideType, str] = {
    SlideType.TITLE: "Title slide",
    SlideType.SECTION: "Section divider",
    SlideType.CONTENT: "Content slide",
    SlideType.TWO_COLUMN: "Two-column layout",
    SlideType.PROS_CONS: "Pros and cons comparison",
    SlideType.CHART: "Chart slide",
    SlideType.DIAGRAM: "Diagram slide",
    SlideType.BLANK: "Blank slide",
    SlideType.COMPARISON: "Multi-column comparison",
    SlideType.TABLE: "Table slide",
    SlideType.QUOTE: "Quote slide",
    SlideType.BIG_NUMBER: "Big number highlight",
    SlideType.PROCESS: "Process flow",
    SlideType.TIMELINE: "Timeline",
    SlideType.STEP_CARDS: "Step cards",
    SlideType.DASHBOARD: "KPI dashboard",
    SlideType.IMAGE_TEXT: "Image with text",
    SlideType.ICON_GRID: "Icon grid",
    SlideType.CARD_GRID: "Card grid",
    SlideType.TEAM: "Team slide",
    SlideType.CLOSING: "Closing slide",
    SlideType.SWOT: "SWOT analysis",
    SlideType.FUNNEL: "Funnel diagram",
    SlideType.AGENDA: "Agenda",
    # Diagram types (v0.6.0)
    SlideType.ORG_CHART: "Organization chart",
    SlideType.MATRIX: "2x2 matrix",
    SlideType.PYRAMID: "Pyramid diagram",
    SlideType.VENN: "Venn diagram",
    SlideType.WATERFALL: "Waterfall chart",
    SlideType.HUB_SPOKE: "Hub-and-spoke diagram",
    SlideType.CYCLE: "Cycle diagram",
    SlideType.GANTT: "Gantt chart",
}


def _build_detail(slide: SlideDefinition) -> str:
    """Build type-specific detail string for a slide description."""
    parts: list[str] = []

    if slide.slide_type == SlideType.DASHBOARD and slide.kpis:
        labels = [k.label for k in slide.kpis[:6]]
        parts.append(f"with {len(slide.kpis)} KPI cards ({', '.join(labels)})")

    elif slide.slide_type == SlideType.CARD_GRID and slide.cards:
        titles = [c.title for c in slide.cards[:6]]
        parts.append(f"with {len(slide.cards)} cards covering {', '.join(titles)}")

    elif slide.slide_type == SlideType.TWO_COLUMN and slide.columns:
        col_titles = [c.title or "Column" for c in slide.columns[:2]]
        parts.append(f"comparing {' and '.join(col_titles)}")

    elif slide.slide_type == SlideType.PROCESS and slide.steps:
        active = next((i for i, s in enumerate(slide.steps) if s.status == "active"), None)
        detail = f"showing {len(slide.steps)} steps"
        if active is not None:
            detail += f", currently at step {active + 1}"
        parts.append(detail)

    elif slide.slide_type == SlideType.TIMELINE and slide.steps:
        parts.append(f"with {len(slide.steps)} milestones")

    elif slide.slide_type == SlideType.TABLE and slide.elements:
        from pptx_mcp.models.slides import TableElement
        for elem in slide.elements:
            if isinstance(elem, TableElement) and elem.rows:
                rows = len(elem.rows)
                cols = max(len(r) for r in elem.rows) if elem.rows else 0
                parts.append(f"with a {rows}x{cols} data table")
                break

    elif slide.slide_type == SlideType.CHART and slide.elements:
        from pptx_mcp.models.slides import ChartElement
        for elem in slide.elements:
            if isinstance(elem, ChartElement):
                chart_type = elem.chart_type.value if elem.chart_type else "chart"
                chart_title = elem.title or slide.title or ""
                parts.append(f"with a {chart_type} chart titled {chart_title}")
                break

    elif slide.slide_type == SlideType.COMPARISON and slide.columns:
        col_titles = [c.title or "Option" for c in slide.columns[:4]]
        parts.append(f"comparing {', '.join(col_titles)}")

    elif slide.slide_type == SlideType.TEAM and slide.team_members:
        names = [m.name for m in slide.team_members[:4]]
        parts.append(f"featuring {', '.join(names)}")

    elif slide.slide_type == SlideType.SWOT and slide.swot:
        total = (len(slide.swot.strengths) + len(slide.swot.weaknesses)
                 + len(slide.swot.opportunities) + len(slide.swot.threats))
        parts.append(f"with {total} points across 4 quadrants")

    elif slide.slide_type == SlideType.FUNNEL and slide.funnel_stages:
        parts.append(f"with {len(slide.funnel_stages)} stages")

    elif slide.slide_type == SlideType.AGENDA and slide.agenda_items:
        parts.append(f"with {len(slide.agenda_items)} items")

    elif slide.slide_type == SlideType.ICON_GRID and slide.icons:
        parts.append(f"with {len(slide.icons)} icons")

    elif slide.slide_type == SlideType.STEP_CARDS and slide.steps:
        parts.append(f"with {len(slide.steps)} step cards")

    elif slide.slide_type == SlideType.QUOTE:
        attr = slide.quote_attribution
        if attr:
            parts.append(f"attributed to {attr}")

    elif slide.slide_type == SlideType.BIG_NUMBER:
        if slide.body:
            parts.append(f"highlighting {slide.body}")

    elif slide.slide_type == SlideType.CONTENT and slide.bullets:
        parts.append(f"with {len(slide.bullets)} bullet points")

    elif slide.slide_type == SlideType.ORG_CHART and slide.org_chart:
        def _count_nodes(node) -> int:
            return 1 + sum(_count_nodes(c) for c in node.children)
        parts.append(f"with {_count_nodes(slide.org_chart)} nodes rooted at {slide.org_chart.name}")

    elif slide.slide_type == SlideType.MATRIX and slide.matrix:
        labels = [slide.matrix.top_left.label, slide.matrix.top_right.label,
                  slide.matrix.bottom_left.label, slide.matrix.bottom_right.label]
        parts.append(f"with quadrants: {', '.join(labels)}")

    elif slide.slide_type == SlideType.PYRAMID and slide.pyramid_layers:
        parts.append(f"with {len(slide.pyramid_layers)} layers")

    elif slide.slide_type == SlideType.VENN and slide.venn:
        labels = [c.label for c in slide.venn.circles]
        parts.append(f"with {len(slide.venn.circles)} circles ({', '.join(labels)})")

    elif slide.slide_type == SlideType.WATERFALL and slide.waterfall_bars:
        parts.append(f"with {len(slide.waterfall_bars)} bars")

    elif slide.slide_type == SlideType.HUB_SPOKE and slide.spokes:
        parts.append(f"with {len(slide.spokes)} spokes around '{slide.hub_spoke_center or 'center'}'")

    elif slide.slide_type == SlideType.CYCLE and slide.cycle_steps:
        parts.append(f"with {len(slide.cycle_steps)} steps")

    elif slide.slide_type == SlideType.GANTT and slide.gantt_tasks:
        parts.append(f"with {len(slide.gantt_tasks)} tasks")

    return " ".join(parts) if parts else ""


def _gather_slide_text(slide: SlideDefinition) -> str:
    """Gather all visible text from a slide for word counting."""
    parts: list[str] = []
    if slide.title:
        parts.append(slide.title)
    if slide.subtitle:
        parts.append(slide.subtitle)
    if slide.body:
        parts.append(slide.body)
    parts.extend(slide.bullets)
    parts.extend(slide.pros)
    parts.extend(slide.cons)
    for col in slide.columns:
        if col.title:
            parts.append(col.title)
        parts.extend(col.bullets)
    for card in slide.cards:
        parts.append(card.title)
        if card.body:
            parts.append(card.body)
        parts.extend(card.bullets)
    for kpi in slide.kpis:
        parts.append(kpi.value)
        parts.append(kpi.label)
    for icon in slide.icons:
        parts.append(icon.title)
        if icon.description:
            parts.append(icon.description)
    for member in slide.team_members:
        parts.append(member.name)
        if member.role:
            parts.append(member.role)
    if slide.callout:
        if slide.callout.title:
            parts.append(slide.callout.title)
        parts.append(slide.callout.body)
    for stage in slide.funnel_stages:
        parts.append(stage.label)
    for item in slide.agenda_items:
        parts.append(item.title)
    if slide.swot:
        parts.extend(slide.swot.strengths)
        parts.extend(slide.swot.weaknesses)
        parts.extend(slide.swot.opportunities)
        parts.extend(slide.swot.threats)
    for step in slide.steps:
        parts.append(step.title)
    # Diagram fields (v0.6.0)
    if slide.org_chart:
        def _gather_org_text(node) -> None:
            parts.append(node.name)
            if node.role:
                parts.append(node.role)
            for child in node.children:
                _gather_org_text(child)
        _gather_org_text(slide.org_chart)
    if slide.matrix:
        for q in [slide.matrix.top_left, slide.matrix.top_right,
                   slide.matrix.bottom_left, slide.matrix.bottom_right]:
            parts.append(q.label)
            parts.extend(q.items)
    for layer in slide.pyramid_layers:
        parts.append(layer.label)
    if slide.venn:
        for c in slide.venn.circles:
            parts.append(c.label)
    for bar in slide.waterfall_bars:
        parts.append(bar.label)
    if slide.hub_spoke_center:
        parts.append(slide.hub_spoke_center)
    for spoke in slide.spokes:
        parts.append(spoke.label)
    for cs in slide.cycle_steps:
        parts.append(cs.label)
    for task in slide.gantt_tasks:
        parts.append(task.label)
    return " ".join(parts)
