from __future__ import annotations

import re
from typing import Any, Iterable


def _segment_text(segment: dict[str, Any], diplomatic: bool) -> str:
    kind = segment["type"]
    if kind == "text":
        return segment["text"]
    if kind == "unclear":
        reading = segment.get("reading")
        alternatives = segment.get("alternatives") or []
        if reading:
            return f"[{reading}?]" if diplomatic else reading
        if alternatives:
            value = "/".join(alternatives)
            return f"[{value}?]"
        return "[unreadable]"
    if kind == "illegible":
        extent = segment.get("extent", {})
        quantity = extent.get("quantity")
        unit = extent.get("unit", "unknown")
        label = f"{quantity} {unit}" if quantity else unit
        return f"[illegible: {label}]"
    if kind == "addition":
        reading = segment.get("reading")
        if reading is None:
            return "[unreadable addition]"
        return f"⟨{reading}⟩" if diplomatic else reading
    if kind == "deletion":
        reading = segment.get("reading")
        if not diplomatic:
            return ""
        return f"⟦deleted: {reading or 'unreadable'}⟧"
    if kind == "correction":
        original = segment.get("original") or "unreadable"
        replacement = segment.get("replacement") or "unreadable"
        if diplomatic:
            return f"⟦{original}→{replacement}⟧"
        return "" if replacement == "unreadable" else replacement
    raise ValueError(f"Unknown inline segment type: {kind}")


def render_lines(lines: list[dict[str, Any]], diplomatic: bool = False) -> str:
    rendered: list[str] = []
    for line in lines:
        text = "".join(_segment_text(segment, diplomatic) for segment in line["segments"])
        line_end = line["line_end"]
        if diplomatic:
            if line_end in {
                "hyphen_word_break",
                "hyphen_lexical",
                "hyphen_uncertain",
            }:
                text += "-"
            rendered.append(text)
            continue

        rendered.append(text)
        if line_end == "no_hyphen":
            rendered.append(" ")
        elif line_end == "hyphen_lexical":
            rendered.append("-")
        elif line_end == "hyphen_uncertain":
            rendered.append("[-?]")
        elif line_end == "block_end":
            rendered.append("\n")
        # hyphen_word_break joins without a character.
    if diplomatic:
        return "\n".join(rendered)
    return re.sub(r"[ \t]+\n", "\n", "".join(rendered)).rstrip()


def _render_list_items(items: Iterable[dict[str, Any]], diplomatic: bool) -> str:
    rendered: list[str] = []
    for item in items:
        label = (
            render_lines(item["label_lines"], diplomatic)
            if item.get("label_lines")
            else ""
        )
        content = render_lines(item["content_lines"], diplomatic)
        rendered.append(f"{label} {content}".strip())
    return "\n".join(rendered)


def _render_table_rows(rows: Iterable[dict[str, Any]], diplomatic: bool) -> str:
    rendered: list[str] = []
    for row in rows:
        cells = [render_lines(cell["lines"], diplomatic) for cell in row["cells"]]
        rendered.append(" | ".join(cells))
    return "\n".join(rendered)


def render_part(part: dict[str, Any], diplomatic: bool = False) -> str:
    kind = part["type"]
    if kind in {"text", "annotation"}:
        return render_lines(part["lines"], diplomatic)
    if kind == "list":
        return _render_list_items(part["items"], diplomatic)
    if kind == "table":
        return _render_table_rows(part["rows"], diplomatic)
    raise ValueError(f"Unknown content part type: {kind}")


def render_block(block: dict[str, Any], diplomatic: bool = False) -> str:
    kind = block["type"]
    if kind in {"heading", "text", "annotation", "gap_notice"}:
        return render_lines(block["lines"], diplomatic)
    if kind in {"diary_entry", "letter"}:
        return "\n".join(
            value
            for value in (render_part(part, diplomatic) for part in block["content"])
            if value
        )
    if kind == "list":
        return _render_list_items(block["items"], diplomatic)
    if kind == "table":
        return _render_table_rows(block["rows"], diplomatic)
    if kind == "separator":
        return block["description"]
    if kind == "graphical_feature":
        return f"[graphical feature: {block['description']}]" if diplomatic else ""
    raise ValueError(f"Unknown block type: {kind}")


def render_page(page: dict[str, Any], diplomatic: bool = False) -> str:
    return "\n\n".join(
        value
        for value in (render_block(block, diplomatic) for block in page["blocks"])
        if value
    )


def structured_tail(page: dict[str, Any], characters: int) -> str:
    rendered = render_page(page, diplomatic=True)
    return rendered[-characters:]
