"""The ablation report (docs/07-ablation.md).

Design rules encoded here:

- **Token cost is a column on every row.** It is exact and it is what turns an
  EQUIVALENT verdict into an action.
- **The screening table is labelled screening everywhere it appears** — in the
  header, in the method note, and by never using verdict language in it.
- **The recommendation is deletions, not additions.** `ablate` never proposes
  new config text.
- **Unresolved sections are named and priced**, not silently dropped. "Six
  sections we couldn't resolve, here's what resolving them costs" is a
  service; omitting them is a lie by selection.
"""

from __future__ import annotations

import math

from rich.console import Console

from ablate.ablation.pipeline import AblationResult, ScreenRow
from ablate.cost import ModelPrice, config_cost_per_turn
from ablate.report.terminal import _rule, _width, _wrap
from ablate.stats import Verdict


def _beta(row: ScreenRow) -> str:
    return f"{row.effect.beta:+.3f}"


def _interval(row: ScreenRow) -> str:
    effect = row.effect
    if not effect.has_ci:
        return "no interval"
    return f"[{effect.lo:+.3f}, {effect.hi:+.3f}]"


def _q(row: ScreenRow) -> str:
    if math.isnan(row.q_value):
        return "—"
    star = " *" if row.significant else ""
    return f"{row.q_value:.2f}{star}"


def _render_screening(console: Console, result: AblationResult) -> None:
    screening = result.screening
    console.print()
    console.print(
        f"  [bold]SCREENING[/bold]  [dim]({screening.n_records} runs, {screening.model}, "
        f"N=1 per cell) — ranking only, not confirmatory[/dim]"
    )
    console.print()

    width = min(max((len(r.section.title) for r in screening.rows), default=20), 30)
    console.print(
        f"    [dim]{'id':<5}{'section':<{width + 2}}{'tokens':>8}{'β pass':>9}"
        f"{'95% CI':>20}{'FDR':>8}[/dim]"
    )

    for row in screening.ranked:
        title = row.section.title
        title = title if len(title) <= width else title[: width - 1] + "…"
        console.print(
            f"    {row.section.id:<5}{title:<{width + 2}}{row.tokens:>8,}"
            f"{_beta(row):>9}{_interval(row):>20}{_q(row):>8}"
        )

    console.print()
    console.print(
        f"    [dim]Empty-config floor: {screening.floor_pass_rate:.2f} pass rate  ·  "
        f"Full config: {screening.reference_pass_rate:.2f}  ·  "
        f"Δ = {screening.floor_delta:+.2f}[/dim]"
    )

    if screening.fit.ci_withheld:
        console.print(f"    [yellow]{screening.fit.ci_withheld}[/yellow]")

    for chunk in _wrap(
        "β is the marginal effect of keeping a section, penalized by ridge and "
        "therefore shrunk toward zero — read the ranking, not the magnitudes. "
        "Screening intervals are approximate; nothing here is a verdict.",
        width=_width(console) - 6,
    ):
        console.print(f"    [dim]{chunk}[/dim]")


def _render_confirmations(console: Console, result: AblationResult) -> None:
    if not result.confirmations:
        return

    first = result.confirmations[0].analysis
    console.print()
    console.print(
        f"  [bold]CONFIRMED[/bold]  [dim]({first.n_records} runs, "
        f"{', '.join(first.models) or 'target model'}, N={first.n_trials}, "
        f"paired, pre-specified)[/dim]"
    )
    console.print()

    width = min(
        max((len(c.section.title) for c in result.confirmations), default=20), 30
    )
    console.print(
        f"    [dim]{'dropping…':<{width + 8}}{'Δ pass rate':>22}{'Δ tokens':>14}"
        f"{'verdict':>16}[/dim]"
    )

    for confirmation in result.confirmations:
        analysis = confirmation.analysis
        pass_rate = analysis.metric("pass_rate")
        tokens = analysis.metric("tokens_per_turn")
        verdict = confirmation.pass_rate_verdict

        title = confirmation.section.title
        title = title if len(title) <= width else title[: width - 1] + "…"
        label = f"{confirmation.section.id} {title}"

        # No sign flip needed: arm A is the config *without* this section, so
        # Δ already reads as "the effect of dropping it".
        if pass_rate and pass_rate.verdict.has_ci:
            delta = f"{pass_rate.verdict.delta * 100:+.0f}pp"
            interval = f"[{pass_rate.verdict.lo * 100:+.0f}, {pass_rate.verdict.hi * 100:+.0f}]"
            pass_cell = f"{delta} {interval}"
        else:
            pass_cell = "no interval"

        token_cell = (
            f"{tokens.verdict.delta * 100:+.0f}%" if tokens and tokens.verdict.has_ci else "—"
        )
        console.print(
            f"    {label:<{width + 8}}{pass_cell:>22}{token_cell:>14}"
            f"[{verdict.colour}]{verdict.value:>16}[/{verdict.colour}]"
        )


def _render_recommendation(
    console: Console, result: AblationResult, price: ModelPrice | None
) -> None:
    console.print()
    console.print(f"[dim]{_rule(console)}[/dim]")
    console.print()
    console.print("  [bold]RECOMMENDATION[/bold]")
    console.print()

    def say(text: str, style: str = "") -> None:
        for chunk in _wrap(text, width=_width(console) - 6):
            console.print(f"    [{style}]{chunk}[/{style}]" if style else f"    {chunk}")
        console.print()

    deletions = result.deletions
    if deletions:
        ids = ", ".join(c.section.id for c in deletions)
        saved = result.tokens_saved
        share = saved / result.total_tokens if result.total_tokens else 0.0
        clause = ""
        if price is not None:
            clause = (
                f", about ${config_cost_per_turn(saved, price) * 1000:.2f} per "
                f"1,000 turns"
            )
        say(
            f"Delete {ids} — ~{saved:,} tokens per turn ({share * 100:.0f}% of the "
            f"file){clause}, with no detectable effect on pass rate.",
            "green",
        )
    else:
        say(
            "No section was confirmed safe to delete. That is a finding, not a "
            "failure: nothing here is dead weight that this suite can prove is "
            "dead weight.",
            "blue",
        )

    for confirmation in result.keeps:
        metric = confirmation.analysis.metric("pass_rate")
        direction = (
            "measurably regresses"
            if confirmation.pass_rate_verdict is Verdict.REGRESSION
            else "measurably improves"
        )
        magnitude = (
            f" by {abs(metric.verdict.delta) * 100:.0f}pp" if metric and metric.verdict.has_ci else ""
        )
        say(
            f"Keep {confirmation.section.id} ({confirmation.section.title}) — "
            f"dropping it {direction} pass rate{magnitude}.",
            "red",
        )

    for confirmation in result.unresolved:
        metric = confirmation.analysis.metric("pass_rate")
        label = f"{confirmation.section.id} ({confirmation.section.title})"

        if metric and metric.verdict.excludes_zero:
            # Not underpowered — the interval is entirely on one side of zero.
            # Saying "could not tell whether it matters" beside an interval like
            # [-57, -28] would be self-evidently wrong.
            delta = metric.verdict.delta * 100
            say(
                f"Do not delete {label} — dropping it changes pass rate by "
                f"{delta:+.0f}pp [{metric.verdict.lo * 100:+.0f}, "
                f"{metric.verdict.hi * 100:+.0f}], which is a real effect. It is "
                f"only 'unresolved' against the ±{metric.verdict.margin * 100:.0f}pp "
                f"margin you set; lower --margin to settle it on this data.",
                "red",
            )
            continue

        requirement = metric.verdict.requirement if metric else None
        priced = ""
        if requirement is not None and requirement.feasible:
            priced = (
                f" Resolving it needs roughly {requirement.n_trials} trials × "
                f"{requirement.n_tasks} tasks ({requirement.n_runs:,} runs)."
            )
        say(
            f"{label} is unresolved — the confirmatory run could not tell whether "
            f"dropping it matters, so it stays.{priced}",
            "yellow",
        )

    unscreened = result.unscreened
    if unscreened:
        tokens = sum(r.tokens for r in unscreened)
        say(
            f"Remaining {len(unscreened)} section{'s' if len(unscreened) != 1 else ''} "
            f"(~{tokens:,} tokens): screening only. They were not confirmed, so "
            f"nothing here says whether they earn their keep — run "
            f"`ablate compare` against each to find out.",
            "dim",
        )

    if deletions:
        ids = ",".join(c.section.id for c in deletions)
        console.print(f"    [bold]ablate apply --drop {ids} {result.config_path}[/bold]")
        console.print()


def render_ablation(
    console: Console,
    result: AblationResult,
    *,
    command: str = "",
    version: str = "",
    price: ModelPrice | None = None,
) -> None:
    """Render the full ablation report."""
    console.print()
    if command:
        console.print(f"  [dim]$ {command}[/dim]")

    n_sections = len(result.sections)
    pinned = sum(1 for s in result.sections if s.required)
    header = f"{result.config_path} — {n_sections} sections, ~{result.total_tokens:,} tokens/turn"
    console.print(f"  [bold]{header}[/bold]")
    if pinned:
        console.print(
            f"  [dim]{pinned} section{'s' if pinned != 1 else ''} pinned with "
            f"<!-- ablate:keep --> and never dropped[/dim]"
        )
    console.print()
    console.print(f"[dim]{_rule(console)}[/dim]")

    _render_screening(console, result)
    console.print()
    console.print(f"[dim]{_rule(console)}[/dim]")
    _render_confirmations(console, result)
    _render_recommendation(console, result, price)

    for chunk in _wrap(
        "Method: randomized subset screening (R = 2K+8 variants, each section kept "
        "at p=0.5), ridge with task fixed effects, λ by leave-one-task-out CV, "
        "Benjamini-Hochberg at q=0.10. Confirmatory rows are paired cluster "
        "bootstrap A/Bs, one pre-specified comparison each.",
        width=_width(console) - 4,
    ):
        console.print(f"  [dim]{chunk}[/dim]")
    console.print()
