"""Layout variant engine — suggests compatible SlideTypes and adapts content between them."""

from __future__ import annotations

import math
from typing import Optional

from pptx_mcp.models.slides import (
    CardDefinition,
    ColumnContent,
    IconItem,
    KpiCard,
    ProcessStep,
    SlideDefinition,
    SlideType,
)

# ---------------------------------------------------------------------------
# Variant groups — each group contains types that can be adapted between.
# ---------------------------------------------------------------------------

VARIANT_GROUPS: dict[str, list[SlideType]] = {
    "structured_content": [
        SlideType.CONTENT,
        SlideType.TWO_COLUMN,
        SlideType.COMPARISON,
        SlideType.PROS_CONS,
    ],
    "sequential_steps": [
        SlideType.PROCESS,
        SlideType.TIMELINE,
        SlideType.STEP_CARDS,
    ],
    "grids": [
        SlideType.DASHBOARD,
        SlideType.CARD_GRID,
        SlideType.ICON_GRID,
    ],
}

# Reverse lookup: SlideType → group name
_TYPE_TO_GROUP: dict[SlideType, str] = {}
for _group_name, _types in VARIANT_GROUPS.items():
    for _t in _types:
        _TYPE_TO_GROUP[_t] = _group_name

# ---------------------------------------------------------------------------
# Human-readable type descriptions
# ---------------------------------------------------------------------------

_TYPE_DESCRIPTIONS: dict[SlideType, str] = {
    SlideType.CONTENT: "Bullet list with optional body text",
    SlideType.TWO_COLUMN: "Side-by-side two-column layout",
    SlideType.COMPARISON: "Multi-column comparison with headers",
    SlideType.PROS_CONS: "Two-column pros vs cons layout",
    SlideType.PROCESS: "Horizontal process flow with status indicators",
    SlideType.TIMELINE: "Vertical timeline with dates and milestones",
    SlideType.STEP_CARDS: "Step-by-step cards in a grid",
    SlideType.DASHBOARD: "KPI metric cards with trends",
    SlideType.CARD_GRID: "Rich cards with title, body, and icon",
    SlideType.ICON_GRID: "Icon grid with title and description",
}


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def get_compatible_types(slide_type: SlideType) -> list[dict]:
    """Return compatible variant types for *slide_type*.

    Each entry has ``slide_type``, ``group``, ``description``, and
    ``adaptation`` (always ``"lossless"`` for structured_content /
    sequential_steps; may be ``"lossy"`` for some grid conversions).

    Returns an empty list for types not in any variant group.
    """
    group = _TYPE_TO_GROUP.get(slide_type)
    if group is None:
        return []

    results: list[dict] = []
    for t in VARIANT_GROUPS[group]:
        if t == slide_type:
            continue
        adaptation = _adaptation_quality(slide_type, t, group)
        results.append({
            "slide_type": t.value,
            "group": group,
            "description": _TYPE_DESCRIPTIONS.get(t, ""),
            "adaptation": adaptation,
        })
    return results


def adapt_slide(
    source: SlideDefinition,
    target_type: SlideType,
) -> Optional[SlideDefinition]:
    """Adapt *source* to *target_type*, preserving content where possible.

    Returns a new ``SlideDefinition`` with adapted fields, or ``None`` if
    the types are in different groups (incompatible).
    """
    if source.slide_type == target_type:
        return source.model_copy()

    src_group = _TYPE_TO_GROUP.get(source.slide_type)
    tgt_group = _TYPE_TO_GROUP.get(target_type)

    if src_group is None or tgt_group is None or src_group != tgt_group:
        return None

    # Preserve common fields
    common = {
        "slide_type": target_type,
        "title": source.title,
        "title_style": source.title_style,
        "subtitle": source.subtitle,
        "subtitle_style": source.subtitle_style,
        "speaker_notes": source.speaker_notes,
        "callout": source.callout,
        "background_color": source.background_color,
        "background_gradient": source.background_gradient,
        "footnotes": source.footnotes,
    }

    adapter = _GROUP_ADAPTERS.get(src_group)
    if adapter is None:
        return None

    adapted_fields = adapter(source, target_type)
    if adapted_fields is None:
        return None
    common.update(adapted_fields)
    return SlideDefinition(**common)


def generate_hint(source: SlideDefinition, target_type: SlideType) -> str:
    """Generate a human-readable hint about what adaptation will do."""
    src_group = _TYPE_TO_GROUP.get(source.slide_type)
    tgt_group = _TYPE_TO_GROUP.get(target_type)
    if src_group != tgt_group or src_group is None:
        return "Incompatible types — content will be re-generated"

    if src_group == "structured_content":
        return _hint_structured(source, target_type)
    elif src_group == "sequential_steps":
        return _hint_sequential(source, target_type)
    elif src_group == "grids":
        return _hint_grids(source, target_type)
    return ""


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


def _extract_bullets(defn: SlideDefinition) -> list[str]:
    """Collect all text content from bullets/columns/pros/cons into a flat list."""
    if defn.bullets:
        return list(defn.bullets)
    if defn.columns:
        result: list[str] = []
        for col in defn.columns:
            result.extend(col.bullets)
        return result
    if defn.pros or defn.cons:
        return list(defn.pros) + list(defn.cons)
    return []


def _split_into_columns(bullets: list[str], n: int) -> list[list[str]]:
    """Evenly distribute *bullets* into *n* groups."""
    if n <= 0:
        return []
    if not bullets:
        return [[] for _ in range(n)]
    size = math.ceil(len(bullets) / n)
    return [bullets[i * size : (i + 1) * size] for i in range(n)]


def _adaptation_quality(
    source: SlideType, target: SlideType, group: str
) -> str:
    """Determine if adaptation is lossless or lossy."""
    if group in ("structured_content", "sequential_steps"):
        return "lossless"
    # Grid group has some lossy conversions
    lossy_pairs = {
        (SlideType.DASHBOARD, SlideType.ICON_GRID),
        (SlideType.ICON_GRID, SlideType.DASHBOARD),
        (SlideType.CARD_GRID, SlideType.DASHBOARD),
    }
    if (source, target) in lossy_pairs:
        return "lossy"
    return "lossless"


# ---------------------------------------------------------------------------
# Group A: Structured Content adapters
# ---------------------------------------------------------------------------


def _adapt_structured(
    source: SlideDefinition, target: SlideType
) -> dict:
    """Adapt between CONTENT, TWO_COLUMN, COMPARISON, PROS_CONS."""
    bullets = _extract_bullets(source)

    if target == SlideType.CONTENT:
        return {"bullets": bullets, "body": source.body}

    if target == SlideType.TWO_COLUMN:
        if source.slide_type == SlideType.COMPARISON and source.columns:
            # Take first 2 columns
            cols = source.columns[:2]
        elif source.slide_type == SlideType.PROS_CONS:
            cols = [
                ColumnContent(title="Pros", bullets=list(source.pros)),
                ColumnContent(title="Cons", bullets=list(source.cons)),
            ]
        else:
            groups = _split_into_columns(bullets, 2)
            cols = [ColumnContent(bullets=g) for g in groups]
        return {"columns": cols}

    if target == SlideType.COMPARISON:
        if source.slide_type in (SlideType.TWO_COLUMN, SlideType.COMPARISON) and source.columns:
            return {"columns": list(source.columns)}
        if source.slide_type == SlideType.PROS_CONS:
            cols = [
                ColumnContent(title="Pros", bullets=list(source.pros)),
                ColumnContent(title="Cons", bullets=list(source.cons)),
            ]
            return {"columns": cols}
        # CONTENT → COMPARISON
        groups = _split_into_columns(bullets, 2)
        cols = [ColumnContent(bullets=g) for g in groups]
        return {"columns": cols}

    if target == SlideType.PROS_CONS:
        if source.slide_type in (SlideType.TWO_COLUMN, SlideType.COMPARISON) and source.columns:
            pros = list(source.columns[0].bullets) if len(source.columns) > 0 else []
            cons = list(source.columns[1].bullets) if len(source.columns) > 1 else []
            return {"pros": pros, "cons": cons}
        # CONTENT → PROS_CONS: split in half
        groups = _split_into_columns(bullets, 2)
        return {"pros": groups[0] if groups else [], "cons": groups[1] if len(groups) > 1 else []}

    return {}


# ---------------------------------------------------------------------------
# Group B: Sequential Steps adapters
# ---------------------------------------------------------------------------

_DEFAULT_DATES = ["Q1", "Q2", "Q3", "Q4"]


def _adapt_sequential(
    source: SlideDefinition, target: SlideType
) -> dict:
    """Adapt between PROCESS, TIMELINE, STEP_CARDS."""
    steps = list(source.steps) if source.steps else []

    if target == SlideType.TIMELINE:
        # Populate date if missing
        for i, step in enumerate(steps):
            if not step.date:
                steps[i] = step.model_copy(
                    update={"date": _DEFAULT_DATES[i % len(_DEFAULT_DATES)]}
                )
        return {"steps": steps}

    if target == SlideType.PROCESS:
        result: dict = {"steps": steps}
        if source.active_step is not None:
            result["active_step"] = source.active_step
        elif source.slide_type == SlideType.STEP_CARDS:
            result["active_step"] = 0
        return result

    if target == SlideType.STEP_CARDS:
        return {"steps": steps}

    return {}


# ---------------------------------------------------------------------------
# Group C: Grids adapters
# ---------------------------------------------------------------------------


def _adapt_grids(
    source: SlideDefinition, target: SlideType
) -> dict:
    """Adapt between DASHBOARD, CARD_GRID, ICON_GRID."""

    if source.slide_type == SlideType.DASHBOARD:
        kpis = list(source.kpis) if source.kpis else []
        if target == SlideType.CARD_GRID:
            cards = [
                CardDefinition(
                    title=k.label,
                    body=k.value,
                    badge=k.badge,
                )
                for k in kpis
            ]
            return {"cards": cards}
        if target == SlideType.ICON_GRID:
            icons = [
                IconItem(
                    icon="\u2b24",  # default circle icon
                    title=k.label,
                    description=k.value,
                )
                for k in kpis
            ]
            return {"icons": icons}

    elif source.slide_type == SlideType.CARD_GRID:
        card_list = list(source.cards) if source.cards else []
        if target == SlideType.DASHBOARD:
            kpis = [
                KpiCard(
                    label=c.title,
                    value=c.body or "—",
                )
                for c in card_list
            ]
            return {"kpis": kpis}
        if target == SlideType.ICON_GRID:
            icons = [
                IconItem(
                    icon=c.icon or "\u2b24",
                    title=c.title,
                    description=c.body or "",
                )
                for c in card_list
            ]
            return {"icons": icons}

    elif source.slide_type == SlideType.ICON_GRID:
        icon_list = list(source.icons) if source.icons else []
        if target == SlideType.CARD_GRID:
            cards = [
                CardDefinition(
                    title=ic.title,
                    icon=ic.icon,
                    body=ic.description or "",
                )
                for ic in icon_list
            ]
            return {"cards": cards}
        if target == SlideType.DASHBOARD:
            kpis = [
                KpiCard(
                    label=ic.title,
                    value=ic.description or "—",
                )
                for ic in icon_list
            ]
            return {"kpis": kpis}

    return None  # unhandled source/target combination


# ---------------------------------------------------------------------------
# Group adapter dispatch
# ---------------------------------------------------------------------------

_GROUP_ADAPTERS = {
    "structured_content": _adapt_structured,
    "sequential_steps": _adapt_sequential,
    "grids": _adapt_grids,
}


# ---------------------------------------------------------------------------
# Hint generators
# ---------------------------------------------------------------------------


def _hint_structured(source: SlideDefinition, target: SlideType) -> str:
    bullets = _extract_bullets(source)
    n = len(bullets)
    if target == SlideType.TWO_COLUMN:
        half = math.ceil(n / 2)
        return f"Your {n} bullets will be split into 2 columns of {half} and {n - half}"
    if target == SlideType.COMPARISON:
        half = math.ceil(n / 2)
        return f"Your {n} bullets will be split into 2 comparison columns"
    if target == SlideType.PROS_CONS:
        half = math.ceil(n / 2)
        return f"Your {n} bullets will be split into {half} pros and {n - half} cons"
    if target == SlideType.CONTENT:
        return f"Your content will be merged into {n} bullets"
    return ""


def _hint_sequential(source: SlideDefinition, target: SlideType) -> str:
    n = len(source.steps) if source.steps else 0
    if target == SlideType.TIMELINE:
        return f"Your {n} steps will become timeline entries with dates"
    if target == SlideType.PROCESS:
        return f"Your {n} steps will become a horizontal process flow"
    if target == SlideType.STEP_CARDS:
        return f"Your {n} steps will become step cards in a grid"
    return ""


def _hint_grids(source: SlideDefinition, target: SlideType) -> str:
    if source.slide_type == SlideType.DASHBOARD:
        n = len(source.kpis)
        if target == SlideType.CARD_GRID:
            return f"Your {n} KPIs will become {n} cards"
        if target == SlideType.ICON_GRID:
            return f"Your {n} KPIs will become {n} icon items"
    elif source.slide_type == SlideType.CARD_GRID:
        n = len(source.cards)
        if target == SlideType.DASHBOARD:
            return f"Your {n} cards will become {n} KPI metrics"
        if target == SlideType.ICON_GRID:
            return f"Your {n} cards will become {n} icon items"
    elif source.slide_type == SlideType.ICON_GRID:
        n = len(source.icons)
        if target == SlideType.CARD_GRID:
            return f"Your {n} icons will become {n} cards"
        if target == SlideType.DASHBOARD:
            return f"Your {n} icons will become {n} KPI metrics"
    return ""
