"""Layer 2 — bootstrap installs the excepthooks; read the report back.

Realistic crash-reporting wiring: ``mgf.common.bootstrap()`` installs
both excepthooks (sys + threading) as part of its transactional
lifecycle, so a previously-uncaught exception (main thread or worker
thread) automatically lands in <state>/crashes/ as a structured JSON
report.
"""

from __future__ import annotations

import json
import os
import sys
import tempfile
import threading
from pathlib import Path

import mgf.common
from mgf.common.observability import report_now


def main() -> None:
    with tempfile.TemporaryDirectory() as tmp:
        os.environ["XDG_STATE_HOME"] = tmp
        os.environ["HOME"] = tmp

        with mgf.common.bootstrap(
            app_name="example-practical-crash", app_version="0.1.0",
        ):
            # bootstrap() installs the sys + threading excepthooks
            # transactionally as part of its lifecycle wiring — no
            # separate install_excepthook calls needed.

            # ── 1. Direct call to report_now (in-band) ─────────────
            try:
                raise ValueError("explicit report")
            except ValueError as e:
                report_now(e, source="demo")

            # ── 2. Worker-thread crash routed by threading_excepthook ─
            def _bad_worker() -> None:
                raise RuntimeError("worker thread crash")

            t = threading.Thread(target=_bad_worker, name="demo-worker")
            t.start()
            t.join()  # wait for the hook to fire

            # ── 3. Simulate an uncaught main-thread exception ──────
            # Without a real `raise`, simulate it by invoking the
            # installed sys.excepthook directly so the example doesn't
            # actually exit non-zero.
            try:
                raise KeyError("simulated unhandled exception")
            except KeyError as e:
                sys.excepthook(type(e), e, e.__traceback__)

            # Read every report back.
            crash_dir = Path(tmp) / mgf.common.app_name() / "crashes"
            files = sorted(crash_dir.glob("*.json"))
            print(f"\nWrote {len(files)} crash reports to {crash_dir}:")
            for f in files:
                r = json.loads(f.read_text())
                print(
                    f"  {f.name}: source={r['source']!r} "
                    f"type={r['exception']['type']} msg={r['exception']['message']!r}"
                )


if __name__ == "__main__":
    main()
