from __future__ import annotations

from .deck_models import TABLE_CARD_MAX_COLUMNS, TABLE_CARD_MAX_ROWS


def resolve_deck_title(spec) -> str | None:
    for slide in spec.slides:
        if slide.type == "title" and slide.title:
            return slide.title
    for slide in spec.slides:
        if slide.title:
            return slide.title
    return None


def build_deck_markdown_preview(spec) -> str:
    lines: list[str] = []
    for slide_index, slide in enumerate(spec.slides, start=1):
        if lines:
            lines.append("")
        lines.extend(_build_slide_markdown_preview_lines(slide, slide_index))
    return _trim_preview("\n".join(lines).strip())


def build_deck_text_preview(spec) -> str:
    lines: list[str] = []
    for slide_index, slide in enumerate(spec.slides, start=1):
        if lines:
            lines.append("")
        lines.extend(_build_slide_text_preview_lines(slide, slide_index))
    return _trim_preview("\n".join(lines).strip())


def _build_slide_markdown_preview_lines(slide, slide_index: int) -> list[str]:
    heading_level = "#" if slide_index == 1 else "##"
    lines = [f"{heading_level} {_resolve_slide_preview_title(slide, slide_index)}"]
    lines.extend(_collect_slide_markdown_preview_lines(slide))
    return lines


def _build_slide_text_preview_lines(slide, slide_index: int) -> list[str]:
    lines = [_resolve_slide_preview_title(slide, slide_index)]
    lines.extend(_collect_slide_text_preview_lines(slide))
    return lines


def _resolve_slide_preview_title(slide, slide_index: int) -> str:
    if slide.title:
        return slide.title
    if slide.type == "quote":
        return "Quote"
    if slide.type == "timeline":
        return f"Timeline Slide {slide_index}"
    if slide.type == "kpi_grid":
        return f"KPI Slide {slide_index}"
    if slide.type == "image":
        return slide.image.image_label if slide.image and slide.image.image_label else f"Image Slide {slide_index}"
    if slide.type == "image_text":
        return (
            slide.image_text.title if slide.image_text and slide.image_text.title else f"Image Text Slide {slide_index}"
        )
    return f"Slide {slide_index}"


def _collect_slide_markdown_preview_lines(slide) -> list[str]:
    lines: list[str] = []

    for item in _collect_slide_preview_items(slide):
        lines.append(f"- {item}")

    if slide.type == "columns":
        for column in slide.columns:
            lines.extend(_build_card_table_markdown_preview_lines(column.title, column.table))
    elif slide.type == "comparison":
        if slide.left:
            lines.extend(_build_card_table_markdown_preview_lines(slide.left.title, slide.left.table))
        if slide.right:
            lines.extend(_build_card_table_markdown_preview_lines(slide.right.title, slide.right.table))

    return lines


def _collect_slide_text_preview_lines(slide) -> list[str]:
    lines: list[str] = []

    for item in _collect_slide_preview_items(slide):
        lines.append(f"- {item}")

    if slide.type == "columns":
        for column in slide.columns:
            lines.extend(_build_card_table_text_preview_lines(column.title, column.table))
    elif slide.type == "comparison":
        if slide.left:
            lines.extend(_build_card_table_text_preview_lines(slide.left.title, slide.left.table))
        if slide.right:
            lines.extend(_build_card_table_text_preview_lines(slide.right.title, slide.right.table))

    return lines


def _append_preview_lines(items: list[str], *texts: str | None, max_lines: int = 3) -> None:
    for text in texts:
        items.extend(_split_preview_lines(text, max_lines=max_lines))


def _collect_columns_preview_items(slide) -> list[str]:
    items: list[str] = []
    for column in slide.columns:
        items.append(column.title)
        _append_preview_lines(items, column.body, max_lines=2)
    return items


def _collect_comparison_preview_items(slide) -> list[str]:
    items: list[str] = []
    for side in (slide.left, slide.right):
        if side is None:
            continue
        items.append(side.title)
        _append_preview_lines(items, side.body, max_lines=2)
    return items


def _collect_timeline_preview_items(slide) -> list[str]:
    items: list[str] = []
    for timeline_item in slide.items:
        items.append(timeline_item.title)
        _append_preview_lines(items, timeline_item.body, max_lines=1)
    return items


def _collect_kpi_grid_preview_items(slide) -> list[str]:
    items: list[str] = []
    for metric in slide.metrics:
        items.append(f"{metric.value} - {metric.label}")
        _append_preview_lines(items, metric.detail, max_lines=1)
    return items


def _collect_quote_preview_items(slide) -> list[str]:
    items: list[str] = []
    if slide.quote:
        items.append(slide.quote)
    if slide.attribution:
        items.append(slide.attribution)
    return items


def _collect_image_text_preview_items(slide) -> list[str]:
    items: list[str] = []
    if slide.image_text is None:
        return items
    if slide.image_text.title:
        items.append(slide.image_text.title)
    _append_preview_lines(items, slide.image_text.body, max_lines=3)
    if slide.image_text.image_label:
        items.append(slide.image_text.image_label)
    return items


def _collect_image_preview_items(slide) -> list[str]:
    items: list[str] = []
    if slide.image is None:
        return items
    if slide.image.image_label:
        items.append(slide.image.image_label)
    return items


def _collect_slide_preview_items(slide) -> list[str]:
    items: list[str] = []
    _append_preview_lines(items, slide.subtitle, slide.body)

    if slide.type == "columns":
        items.extend(_collect_columns_preview_items(slide))
    elif slide.type == "comparison":
        items.extend(_collect_comparison_preview_items(slide))
    elif slide.type == "timeline":
        items.extend(_collect_timeline_preview_items(slide))
    elif slide.type == "kpi_grid":
        items.extend(_collect_kpi_grid_preview_items(slide))
    elif slide.type == "quote":
        items.extend(_collect_quote_preview_items(slide))
    elif slide.type == "image":
        items.extend(_collect_image_preview_items(slide))
    elif slide.type == "image_text":
        items.extend(_collect_image_text_preview_items(slide))

    return _dedupe_preview_items(items)


def _build_card_table_markdown_preview_lines(title: str, table) -> list[str]:
    if table is None:
        return []
    if _table_preview_should_summarize(table):
        return []

    lines = [f"- {title} table"]
    lines.append(_build_markdown_table_row(table.columns))
    lines.append(_build_markdown_table_separator(len(table.columns)))
    for row in table.rows:
        lines.append(_build_markdown_table_row(row))
    return lines


def _build_card_table_text_preview_lines(title: str, table) -> list[str]:
    if table is None:
        return []
    if _table_preview_should_summarize(table):
        return []

    lines = [f"- {title} table"]
    lines.append("  " + " | ".join(table.columns))
    for row in table.rows:
        lines.append("  " + " | ".join(row))
    return lines


def _build_markdown_table_row(values: list[str]) -> str:
    escaped_values = [value.replace("|", "\\|").replace("\n", " ") for value in values]
    return "| " + " | ".join(escaped_values) + " |"


def _build_markdown_table_separator(column_count: int) -> str:
    return "| " + " | ".join(["---"] * column_count) + " |"


def _table_preview_should_summarize(table) -> bool:
    return len(table.columns) > TABLE_CARD_MAX_COLUMNS or len(table.rows) > TABLE_CARD_MAX_ROWS


def _split_preview_lines(text: str | None, max_lines: int = 3) -> list[str]:
    if not isinstance(text, str) or not text.strip():
        return []
    lines = [line.strip(" -\t") for line in text.splitlines() if line.strip()]
    return lines[:max_lines]


def _dedupe_preview_items(items: list[str]) -> list[str]:
    deduped: list[str] = []
    seen: set[str] = set()
    for item in items:
        normalized = item.strip()
        if not normalized:
            continue
        marker = normalized.casefold()
        if marker in seen:
            continue
        seen.add(marker)
        deduped.append(normalized)
    return deduped[:24]


def _trim_preview(text: str, max_chars: int = 4000) -> str:
    if len(text) <= max_chars:
        return text
    trimmed = text[: max_chars - 3].rstrip()
    return f"{trimmed}..."
