"""Export the token calculator as an LLM-readable report source.

Executes the notebook ONCE (widget defaults — or whatever scenario you've
set and saved in the notebook), then converts the executed copy twice:

  exports/genesys_token_calculator.html — human-reviewable, full presentation
  exports/genesys_token_calculator.md   — leanest LLM input: presentation-tagged
                                          cells (setup, widgets, tables, charts)
                                          are stripped, a framing preamble is
                                          prepended, and the data appendix ends
                                          the file with one fenced JSON block —
                                          the machine source of truth.

Engagement identity (client, date, preparer) is read from the notebook's
``engagement-data`` cell and stamped into the preamble, alongside the
rate-card source date — a cost figure without its rate card is not auditable.

Run from the calculator root:  python scripts/export_report.py
"""
from __future__ import annotations

import subprocess
import sys
import tempfile
from pathlib import Path
from typing import Any

ROOT = Path(__file__).resolve().parent.parent
NOTEBOOK = ROOT / "notebooks" / "genesys_token_calculator.ipynb"
EXPORTS = ROOT / "exports"

# Cells tagged with any of these never reach the .md export — they are stage
# presentation (source and widget-repr noise), not the priced record.
STRIP_TAGS_FROM_MD = '{"presentation"}'


def engagement_data() -> dict[str, Any]:
    """Exec the engagement-data cell (same trick as tests/conftest.py)."""
    import nbformat

    nb = nbformat.read(NOTEBOOK, as_version=4)
    cells = [c for c in nb.cells if "engagement-data" in c.metadata.get("tags", [])]
    assert len(cells) == 1, "expected exactly one engagement-data cell"
    ns: dict[str, Any] = {}
    exec(compile(cells[0].source, f"{NOTEBOOK.name} [engagement-data]", "exec"), ns)
    return ns["ENGAGEMENT"]  # type: ignore[no-any-return]


def preamble() -> str:
    sys.path.insert(0, str(ROOT))
    from genesyscalc.ratecard import RATE_CARD_SOURCE, RATE_CARD_SOURCE_DATE
    from genesyscalc.tts import TTS_SOURCE, TTS_SOURCE_DATE

    eng = engagement_data()
    who = " · ".join(
        str(eng[k]).strip()
        for k in ("client", "prepared_date", "prepared_by")
        if str(eng.get(k, "")).strip()
    )
    line = who or "master copy — illustrative placeholders; no client data."
    return "\n".join(
        [
            "<!-- Export preamble — generated by scripts/export_report.py -->",
            "**What this is** — a costed Genesys Cloud AI scenario, produced from",
            "the Mercury-served token calculator. It is a **planning model**: list",
            "rates unless a contracted rate was entered, and never a quote.",
            "",
            f"**Engagement** — {line}",
            "",
            f"**Rate card** — [{RATE_CARD_SOURCE}]({RATE_CARD_SOURCE}), published",
            f"**{RATE_CARD_SOURCE_DATE}**. Text-to-speech is priced separately from",
            f"[{TTS_SOURCE}]({TTS_SOURCE}) (published {TTS_SOURCE_DATE}) and is",
            "billed per character, not in tokens — it is reported as its own line",
            "and never folded into the token subtotal.",
            "",
            "**How to read it** — first the content as annotated source (the feature",
            "catalogue, then the engagement data), then the scenario state, the",
            "verification gate, and finally the data appendix: the published rate",
            "card, the per-feature cost table, the scenario comparison, the applied",
            "rules and warnings, and — last — **Model state (JSON)**, one fenced",
            "`json` block. Where prose and JSON disagree, the JSON block is the",
            "source of truth; its `rate_card.source_date` says which published rate",
            "card produced these numbers.",
            "",
            "---",
            "",
            "",
        ]
    )


def main() -> None:
    if len(sys.argv) > 1 and sys.argv[1] not in NOTEBOOK.name:
        sys.exit(f"no notebook matches {sys.argv[1]!r}")
    EXPORTS.mkdir(exist_ok=True)

    with tempfile.TemporaryDirectory() as tmp:
        executed = Path(tmp) / NOTEBOOK.name
        # 1. Execute once — both formats convert the same scenario state.
        # (Never combine --execute with TagRemovePreprocessor in one call:
        # the cell could be stripped before it runs.)
        subprocess.run(
            [
                sys.executable, "-m", "nbconvert", "--execute",
                "--to", "notebook", "--output", str(executed), str(NOTEBOOK),
            ],
            check=True, cwd=ROOT,
        )
        # 2. HTML — full presentation, human review artifact.
        subprocess.run(
            [
                sys.executable, "-m", "nbconvert", "--to", "html",
                "--output-dir", str(EXPORTS), "--output", NOTEBOOK.stem,
                str(executed),
            ],
            check=True, cwd=ROOT,
        )
        # 3. Markdown — LLM artifact: strip presentation cells.
        subprocess.run(
            [
                sys.executable, "-m", "nbconvert", "--to", "markdown",
                "--output-dir", str(EXPORTS), "--output", NOTEBOOK.stem,
                "--TagRemovePreprocessor.enabled=True",
                f"--TagRemovePreprocessor.remove_cell_tags={STRIP_TAGS_FROM_MD}",
                str(executed),
            ],
            check=True, cwd=ROOT,
        )

    # 4. Prepend the framing preamble to the markdown export.
    md = EXPORTS / f"{NOTEBOOK.stem}.md"
    md.write_text(preamble() + md.read_text(encoding="utf-8"), encoding="utf-8")

    for p in sorted(EXPORTS.iterdir()):
        if p.suffix in (".html", ".md"):
            print(f"wrote {p.relative_to(ROOT)} ({p.stat().st_size / 1024:,.0f} KB)")


if __name__ == "__main__":
    main()
