"""Plain-text report, printed by the CLI and shown on the web page."""
from __future__ import annotations

from .policies import SKIP


def pct(x, d=1):
    return f"{100 * x:.{d}f}%"


def money(x, unit="$"):
    """Cost per 1,000 prompts in the unit the file used."""
    v = 1000 * x
    if unit == "$":
        return f"${v:,.3f}" if v < 100 else f"${v:,.0f}"
    return f"{v:,.1f} {unit}"


def thresholds_text(models, thr):
    steps = []
    active = [i for i, t in enumerate(thr) if t is not SKIP]
    for k, i in enumerate(active):
        if k == len(active) - 1:
            steps.append(f"{models[i]} answers")
        else:
            steps.append(f"{models[i]} answers if confidence >= {thr[i]:.3f}, else")
    return " -> ".join(steps)


def recipe(report: dict) -> list:
    """How to deploy the cascade, one line per step."""
    cas = report.get("cascade")
    if not cas:
        return []
    models, thr = report["models"], cas["thresholds"]
    active = [i for i, t in enumerate(thr) if t is not SKIP]
    lines = []
    for k, i in enumerate(active):
        if k == len(active) - 1:
            lines.append(f"{k + 1}. Call {models[i]} and return its answer.")
        else:
            lines.append(f"{k + 1}. Call {models[i]}. If its confidence is at least {thr[i]:.3f}, return its answer; otherwise go on.")
    skipped = [models[i] for i in range(len(models)) if i not in active]
    if skipped:
        lines.append(f"Never call: {', '.join(skipped)}.")
    return lines


def verdict(report: dict, unit="$") -> list:
    """The sentences that matter, in order."""
    out = []
    t, d = report["target"], report["delta"]
    conf_lvl = pct(1 - d, 0)
    singles = report["singles"]
    frontier = singles[-1]
    cheapest = singles[0]
    cas = report.get("cascade")
    rtr = report.get("router")
    nh = report["nHold"]
    cert_singles = [s for s in singles if s["hold"]["certified"]]
    best_single = min(cert_singles, key=lambda s: s["hold"]["cost"]) if cert_singles else None

    if cas:
        h = cas["hold"]
        if h["certified"]:
            out.append(f"The cascade is certified: on {nh} held-out prompts it scored {pct(h['quality'])}, and with {conf_lvl} confidence its true quality is at least {pct(h['qualityLB'])} (target {pct(t)}). "
                       f"It costs {money(h['cost'], unit)} per 1k prompts, {pct(h['cost'] / frontier['hold']['cost'], 0)} of {frontier['name']} alone.")
        elif h["quality"] >= t:
            need = h["rowsNeeded"]
            more = "more than 10 million" if need is None or need > 10_000_000 else f"about {need:,}"
            out.append(f"Not certified yet: the cascade scored {pct(h['quality'])} on {nh} held-out prompts (target {pct(t)}), but the {conf_lvl} lower bound is only {pct(h['qualityLB'])}. "
                       f"At this rate you would need {more} held-out prompts to certify it.")
        else:
            out.append(f"The cascade falls short: {pct(h['quality'])} on the held-out prompts against a target of {pct(t)}" +
                       ("" if cas["feasible"] else ", and no cascade reached the target even on the calibration rows") +
                       f". Its {conf_lvl} lower bound is {pct(h['qualityLB'])}. More data will not fix this; a better model or a better confidence signal would.")
        if best_single and h["certified"]:
            if h["cost"] < best_single["hold"]["cost"]:
                out.append(f"The cheapest single model that is certified at this target is {best_single['name']} at {money(best_single['hold']['cost'], unit)} per 1k; the cascade saves {pct(1 - h['cost'] / best_single['hold']['cost'], 0)}.")
            else:
                out.append(f"But {best_single['name']} alone is also certified at this target and costs {money(best_single['hold']['cost'], unit)} per 1k, so just use {best_single['name']}.")
        elif not best_single:
            out.append(f"No single model is certified at {pct(t)} on this data" + (f"; {frontier['name']} alone scored {pct(frontier['hold']['quality'])} with a lower bound of {pct(frontier['hold']['qualityLB'])}." if True else ""))
    elif report["signal"] == "none":
        out.append("No confidence column and no prompt text, so there is nothing to route on. Add a confidence column (the model's own estimate that it is right) or the prompt text, and the cascade and router come back.")
    if rtr:
        h = rtr["hold"]
        state = "certified" if h["certified"] else ("meets the target on the held-out rows but is not certified" if h["quality"] >= t else "falls short")
        out.append(f"The learned router (prompt text only, no second call) {state}: {pct(h['quality'])} quality, lower bound {pct(h['qualityLB'])}, {money(h['cost'], unit)} per 1k.")
    orc = report["oracle"]["hold"]
    out.append(f"Headroom: an oracle that knew in advance which cheap model would be right would reach {pct(orc['quality'])} for {money(orc['cost'], unit)} per 1k. "
               f"{cheapest['name']} alone is {pct(cheapest['hold']['quality'])} for {money(cheapest['hold']['cost'], unit)}.")
    be = report.get("breakEven")
    if be and be.get("winsFrom") is not None:
        out.append(f"The cascade is the cheapest certified option at {be['wins']} of the {be['inRange']} targets between {pct(be['winsFrom'], 0)} and {pct(be['winsTo'], 0)}" +
                   (f"; the highest target it certifies at all is {pct(be['maxCertified'], 0)}." if be.get("maxCertified") is not None else "."))
    elif be and be.get("maxCertified") is not None:
        out.append(f"A single model is at least as cheap at every target; the highest target the cascade certifies is {pct(be['maxCertified'], 0)}.")
    return out


def text_report(report: dict, unit="$") -> str:
    models = report["models"]
    L = []
    L.append(f"Model Router plan  -  target quality {pct(report['target'])}, delta {report['delta']} ({pct(1 - report['delta'], 0)} confidence)")
    L.append(f"{report['n']} prompts x {len(models)} models; {report['nCal']} calibrate, {report['nHold']} certify (seed {report['seed']}); "
             f"gate: {report['signal']}; scores are {'0/1 (Clopper-Pearson)' if report['binary'] else 'in [0,1] (Hoeffding)'}")
    for w in report.get("warnings", []):
        L.append("note: " + w)
    L.append("")
    L.append(f"{'policy':<22}{'quality':>9}{'lower bd':>10}{'cost/1k':>14}{'cost UB':>12}{'latency':>10}  traffic per model ({' / '.join(models)})")
    rows = [(s["name"], s["hold"]) for s in report["singles"]]
    rows.append(("Oracle", report["oracle"]["hold"]))
    if report.get("cascade"):
        rows.append(("Cascade", report["cascade"]["hold"]))
    if report.get("router"):
        rows.append(("Learned router", report["router"]["hold"]))
    for name, h in rows:
        lat = f"{h['latency']:.0f}" if h.get("latency") is not None else "-"
        shares = " / ".join(pct(x, 0) for x in h["shares"])
        flag = "*" if h.get("certified") else " "
        L.append(f"{name:<22}{pct(h['quality']):>9}{pct(h['qualityLB']):>9}{flag}{money(h['cost'], unit):>14}{money(h['costUB'], unit):>12}{lat:>10}  {shares}")
    L.append("* certified: lower bound >= target.  Costs are per 1,000 prompts; cost UB is the Hoeffding upper bound on the mean.")
    L.append("")
    if report.get("cascade"):
        L.append("Cascade: " + thresholds_text(models, report["cascade"]["thresholds"]))
        calls = report["cascade"]["hold"]["calls"]
        L.append("Share of prompts that reach each model: " + ", ".join(f"{m} {pct(c, 0)}" for m, c in zip(models, calls)))
    if report.get("router"):
        L.append(f"Router: pick the cheapest model whose predicted P(correct) >= {report['router']['tau']:.3f}, else {models[-1]}")
    L.append("")
    for s in verdict(report, unit):
        L.append(s)
    sens = report.get("sensitivity") or []
    if sens:
        L.append("")
        L.append(f"{'target':>7}{'cascade/1k':>14}{'lower bd':>10}{'cert.':>6}{'best single':>22}{'oracle/1k':>12}")
        for r in sens:
            c = r.get("cascade")
            bs = r.get("bestSingle")
            L.append(f"{pct(r['target'], 0):>7}" +
                     (f"{money(c['cost'], unit):>14}{pct(c['qualityLB']):>10}{'yes' if c['certified'] else 'no':>6}" if c else f"{'-':>14}{'-':>10}{'-':>6}") +
                     (f"{bs['name'] + ' ' + money(bs['cost'], unit):>22}" if bs else f"{'none certified':>22}") +
                     (f"{money(r['oracleCost'], unit):>12}" if r.get("oracleCost") is not None else f"{'-':>12}"))
    return "\n".join(L)
