"""introspection eval lane runner.

For each case:
  1. Run the prompt on a fresh CognitiveTurnPipeline and capture
     (surface_A, trace_hash_A, turn_id_A).
  2. Attempt to call an `explain(turn_id)` function from
     `core.cognition`.  v1 expects this to raise ImportError; the
     runner catches it and scores M1 = False.
  3. When (2) succeeds, run a fresh pipeline on the produced account
     and capture (surface_B, trace_hash_B).
  4. Score round-trip overlap.

Conforms to the framework interface: run_lane(cases, config=None) -> report.
"""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from typing import Any

from chat.runtime import ChatRuntime
from core.cognition.pipeline import CognitiveTurnPipeline
from core.config import RuntimeConfig
from evals.parallel import run_cases_parallel


@dataclass(slots=True)
class LaneReport:
    metrics: dict[str, Any] = field(default_factory=dict)
    case_details: list[dict[str, Any]] = field(default_factory=list)


_TOKEN_BOUND = re.compile(r"[a-z0-9]+")


def _tokens(text: str) -> set[str]:
    return set(_TOKEN_BOUND.findall((text or "").lower()))


def _try_import_explain():
    """Return the explain callable or None when the API is absent."""
    try:
        from core.cognition import explain  # type: ignore[attr-defined]
    except (ImportError, AttributeError):
        return None
    return explain


def _run_case(case: dict[str, Any]) -> dict[str, Any]:
    prompt: str = case["prompt"]

    runtime = ChatRuntime()
    pipeline = CognitiveTurnPipeline(runtime)
    try:
        result_a = pipeline.run(prompt, max_tokens=12)
    except ValueError:
        return {
            "id": case.get("id", ""),
            "explain_api_present": False,
            "account_nonempty": False,
            "round_trip_surface_match": False,
            "round_trip_trace_match": False,
            "passed": False,
        }

    surface_a = result_a.surface or ""
    trace_a = result_a.trace_hash

    explain = _try_import_explain()
    api_present = explain is not None
    account = ""
    surface_b = ""
    trace_b = ""
    if api_present:
        try:
            account = explain(result_a) or ""  # type: ignore[misc]
        except Exception:
            account = ""
        if account:
            rt2 = ChatRuntime()
            pipe2 = CognitiveTurnPipeline(rt2)
            try:
                result_b = pipe2.run(account, max_tokens=12)
                surface_b = result_b.surface or ""
                trace_b = result_b.trace_hash
            except ValueError:
                pass

    # ≥ 2 tokens — the deterministic canonical form for a DEFINITION probe
    # ("What is X?") is 3 tokens; we accept any account that is plausibly
    # a sentence rather than a single bare token or empty string.  See
    # contract.md.
    account_nonempty = len(_tokens(account)) >= 2
    a_tokens = _tokens(surface_a)
    b_tokens = _tokens(surface_b)
    if a_tokens:
        coverage = len(a_tokens & b_tokens) / len(a_tokens)
    else:
        coverage = 0.0
    surface_match = coverage >= 0.60
    trace_match = bool(trace_a) and trace_a == trace_b

    passed = api_present and account_nonempty and surface_match

    return {
        "id": case.get("id", ""),
        "explain_api_present": api_present,
        "account_nonempty": account_nonempty,
        "round_trip_surface_match": surface_match,
        "round_trip_trace_match": trace_match,
        "surface_token_coverage": round(coverage, 4),
        "passed": passed,
    }


def run_lane(
    cases: list[dict[str, Any]],
    *,
    config: RuntimeConfig | None = None,
    workers: int | None = None,
) -> LaneReport:
    if not cases:
        return LaneReport(metrics={}, case_details=[])
    _ = config

    case_details = run_cases_parallel(cases, _run_case, workers=workers)
    total = len(case_details)

    api = sum(1 for d in case_details if d["explain_api_present"]) / total
    nonempty = sum(1 for d in case_details if d["account_nonempty"]) / total
    surf = sum(1 for d in case_details if d["round_trip_surface_match"]) / total
    trace = sum(1 for d in case_details if d["round_trip_trace_match"]) / total
    overall = sum(1 for d in case_details if d["passed"]) / total

    overall_pass = api >= 0.95 and nonempty >= 0.95 and surf >= 0.50

    metrics: dict[str, Any] = {
        "explain_api_present_rate": round(api, 4),
        "account_nonempty_rate": round(nonempty, 4),
        "round_trip_surface_match_rate": round(surf, 4),
        "round_trip_trace_match_rate": round(trace, 4),
        "all_pass_rate": round(overall, 4),
        "case_count": total,
        "overall_pass": overall_pass,
    }

    return LaneReport(metrics=metrics, case_details=case_details)
