"""GitHub PR-comment diff renderer.

Compact GFM with collapsible <details> blocks. Length-capped to fit GitHub's
65,536-char comment limit (we cap at 60K to leave headroom for any envelope a
caller adds). Truncation prefers to drop modified-component detail first
(most verbose), then added/removed lists."""

from __future__ import annotations

from aa_auto_sdr.snapshot.models import (
    DiffReport,
    ModifiedItem,
)

LENGTH_CAP = 60_000

_TYPE_LABELS = {
    "dimensions": "Dimensions",
    "metrics": "Metrics",
    "segments": "Segments",
    "calculated_metrics": "Calculated Metrics",
    "virtual_report_suites": "Virtual Report Suites",
    "classifications": "Classifications",
}
_INLINE_VALUE_CAP = 200


def render_pr_comment(
    report: DiffReport,
    *,
    summary: bool = False,
    labels: tuple[str, str] | None = None,
) -> str:
    """Render `report` as compact GFM suitable for a GitHub PR comment.

    Output is capped at LENGTH_CAP chars; anything beyond gets truncated at the
    last <details> boundary with a banner. `summary=True` drops per-field
    detail and emits only counts. `labels=(a, b)` adds an "**a** vs **b**"
    line under the header (default `None` preserves v1.1 output exactly)."""
    parts: list[str] = []
    parts.append(_header(report, labels=labels))
    if report.rsid_mismatch:
        parts.append(
            f"> ⚠ RSID mismatch — `{report.a_rsid}` vs `{report.b_rsid}` — "
            "this diff compares different report suites.\n",
        )
    parts.append(_top_counts_line(report))
    parts.append(_breakdown_table(report))
    suppression_annotations = _suppression_annotations(report)
    if suppression_annotations:
        parts.append(suppression_annotations)

    if summary:
        body = "\n".join(parts) + "\n\n---\n*Generated by `aa_auto_sdr`*\n"
        return _enforce_cap(body)

    detail_blocks = _detail_blocks(report)
    parts.extend(b for b in detail_blocks if b)
    body = "\n".join(parts) + "\n\n---\n*Generated by `aa_auto_sdr`*\n"
    return _enforce_cap(body)


def _header(report: DiffReport, *, labels: tuple[str, str] | None = None) -> str:
    short_ts = report.b_captured_at.split("T")[0]
    if labels:
        return f"### SDR Diff: `{report.a_rsid}` ({short_ts})\n— **{labels[0]}** vs **{labels[1]}**\n"
    return f"### SDR Diff: `{report.a_rsid}` ({short_ts})\n"


def _top_counts_line(report: DiffReport) -> str:
    a = sum(len(c.added) for c in report.components if not c.suppressed)
    r = sum(len(c.removed) for c in report.components if not c.suppressed)
    m = sum(len(c.modified) for c in report.components if not c.suppressed)
    return f"- **Added:** {a} | **Removed:** {r} | **Modified:** {m}\n"


def _breakdown_table(report: DiffReport) -> str:
    rows = ["<details>", "<summary>📊 Component breakdown</summary>", ""]
    rows.append("| Type | Added | Removed | Modified | Unchanged |")
    rows.append("|------|-------|---------|----------|-----------|")
    for c in report.components:
        label = _TYPE_LABELS.get(c.component_type, c.component_type)
        if c.suppressed:
            rows.append(f"| {label} | _suppressed_ | _suppressed_ | _suppressed_ | _suppressed_ |")
        else:
            rows.append(f"| {label} | {len(c.added)} | {len(c.removed)} | {len(c.modified)} | {c.unchanged_count} |")
    rows.append("</details>")
    return "\n".join(rows) + "\n"


def _suppression_annotations(report: DiffReport) -> str:
    lines = []
    for cd in report.components:
        if cd.suppressed:
            label = _TYPE_LABELS.get(cd.component_type, cd.component_type)
            lines.append(f"⚠ **{label}** — diff suppressed ({cd.suppression_reason})")
    if not lines:
        return ""
    return "\n".join(lines) + "\n"


def _detail_blocks(report: DiffReport) -> list[str]:
    return [
        _added_block(report),
        _removed_block(report),
        _modified_block(report),
    ]


def _added_block(report: DiffReport) -> str:
    items = [(c.component_type, item) for c in report.components if not c.suppressed for item in c.added]
    if not items:
        return ""
    lines = ["<details>", "<summary>➕ Added components</summary>", ""]  # noqa: RUF001
    for ctype, item in items:
        lines.append(f"- `{item.id}` — {item.name} _({ctype})_")
    lines.append("</details>")
    return "\n".join(lines) + "\n"


def _removed_block(report: DiffReport) -> str:
    items = [(c.component_type, item) for c in report.components if not c.suppressed for item in c.removed]
    if not items:
        return ""
    lines = ["<details>", "<summary>➖ Removed components</summary>", ""]  # noqa: RUF001
    for ctype, item in items:
        lines.append(f"- `{item.id}` — {item.name} _({ctype})_")
    lines.append("</details>")
    return "\n".join(lines) + "\n"


def _modified_block(report: DiffReport) -> str:
    rows: list[tuple[str, ModifiedItem]] = [
        (c.component_type, item) for c in report.components if not c.suppressed for item in c.modified
    ]
    if not rows:
        return ""
    lines = ["<details>", "<summary>✏️ Modified components</summary>", ""]
    for ctype, item in rows:
        lines.append(f"- `{item.id}` — {item.name} _({ctype})_")
        lines.extend(f"  - `{delta.field}`: {_short(delta.before)} → {_short(delta.after)}" for delta in item.deltas)
    lines.append("</details>")
    return "\n".join(lines) + "\n"


def _short(value: object) -> str:
    s = "" if value is None else str(value)
    if len(s) > _INLINE_VALUE_CAP:
        return f"`{s[:_INLINE_VALUE_CAP]}…`"
    return f"`{s}`"


def _enforce_cap(body: str) -> str:
    if len(body) <= LENGTH_CAP:
        return body
    truncated = body[: LENGTH_CAP - 200]
    last_close = truncated.rfind("</details>")
    if last_close >= 0:
        truncated = truncated[: last_close + len("</details>")]
    banner = "\n\n*(truncated — additional changes not shown; pipe full diff to a file with `--format json`)*\n"
    return truncated + banner
