"""X11 citation corroboration — classify treatment, match to halacha, aggregate.

Phase 1: builds the SIGNAL only (no approval changes). See docs/spec/X11.
All LLM calls go through the local claude_session bridge (Opus 4.8 @ xhigh),
same architectural rule as the other extractors (local MCP only).
"""
from __future__ import annotations
import logging
from uuid import UUID
from legal_mcp import config
from legal_mcp.config import parse_llm_json
from legal_mcp.services import claude_session
from legal_mcp.services import db, embeddings

logger = logging.getLogger(__name__)

TREATMENT_POSITIVE = {"followed", "explained"}
TREATMENT_NEGATIVE = {"distinguished", "criticized", "questioned", "overruled"}
TREATMENT_NEUTRAL = {"mentioned"}
_VALID_TREATMENT = TREATMENT_POSITIVE | TREATMENT_NEGATIVE | TREATMENT_NEUTRAL

def is_positive(t: str) -> bool: return t in TREATMENT_POSITIVE
def is_negative(t: str) -> bool: return t in TREATMENT_NEGATIVE

def _coerce_treatment(raw: dict) -> str:
    t = str((raw or {}).get("treatment", "")).strip().lower()
    return t if t in _VALID_TREATMENT else "mentioned"


def accept_match(best: tuple[str, float] | None, floor: float = config.HALACHA_CORROBORATION_MATCH_FLOOR) -> str | None:
    """Return the halacha_id iff similarity clears the floor (INV-COR3)."""
    if not best:
        return None
    halacha_id, sim = best
    return halacha_id if sim >= floor else None


def aggregate(links: list[dict], min_cites: int = config.HALACHA_CORROBORATION_MIN_CITES) -> dict:
    """Aggregate per-halacha corroboration (INV-COR4/COR2).

    links: [{"source_id": str, "treatment": str}, ...] already matched to ONE halacha.
    positive_sources = count of DISTINCT source_id whose treatment is positive.
    has_negative = any negative treatment present.
    corroborated = positive_sources >= min_cites AND not has_negative.
    """
    positive = {l["source_id"] for l in links if is_positive(l["treatment"])}
    has_negative = any(is_negative(l["treatment"]) for l in links)
    return {
        "positive_sources": len(positive),
        "has_negative": has_negative,
        "corroborated": len(positive) >= min_cites and not has_negative,
    }


def approval_action(agg: dict, has_overruled: bool) -> str | None:
    """Decide the corroboration→approval action for ONE halacha (INV-COR2/COR4).

    - 'demote'  : a later court overruled it → back to the chair gate (overruled
                  outranks any positive count, INV-COR2 strong form).
    - 'approve' : corroborated (≥N distinct positives, 0 negatives — INV-COR4).
    - None      : leave as-is (single source, non-overruled negative, or the
                  uncorroborated tail — INV-COR5 keeps the chair gate).
    """
    if has_overruled:
        return "demote"
    if agg.get("corroborated"):
        return "approve"
    return None


_TREATMENT_PROMPT = """אתה משפטן בכיר. נתון ציטוט של פסק/החלטה קודמים בתוך החלטה מאוחרת.
סווג כיצד ההחלטה המאוחרת **מטפלת** בתקדים המצוטט, לפי אחת מהקטגוריות:
- followed   — אימצה והחילה את ההלכה.
- explained  — הסבירה/הזכירה בלי לחלוק.
- distinguished — אבחנה (קבעה שלא חל בנסיבות).
- criticized — מתחה ביקורת בלי לבטל.
- questioned — הטילה ספק.
- overruled  — דחתה/ביטלה את ההלכה.
- mentioned  — אזכור-אגב בלי טיפול.
החזר JSON בלבד: {"treatment": "<קטגוריה>"}.
"""

async def classify_treatment(cited_citation: str, context: str) -> str:
    """Return one treatment label for how `context` treats `cited_citation`."""
    user = f"תקדים מצוטט: {cited_citation}\n\n--- ההקשר המצטט ---\n{context}\n--- סוף ---"
    try:
        result = await claude_session.query_json(
            user, system=_TREATMENT_PROMPT,
            model=config.HALACHA_EXTRACT_MODEL or None,
            effort=config.HALACHA_EXTRACT_EFFORT or None,
            tools="",  # pure text→JSON — no tool_use → no error_max_turns
        )
    except Exception as e:
        logger.warning("classify_treatment failed: %s", e)
        return "mentioned"
    return _coerce_treatment(result if isinstance(result, dict) else {})


async def build_for_precedent(case_law_id: str | UUID) -> dict:
    """For one cited precedent: classify+match+store each incoming citation. Idempotent."""
    if isinstance(case_law_id, str):
        case_law_id = UUID(case_law_id)
    cits = await db.incoming_citations_for_precedent(case_law_id)
    linked = 0
    for c in cits:
        ctx = (c.get("context") or "").strip()
        if not ctx:
            continue
        vecs = await embeddings.embed_texts([ctx], input_type="query")
        best = await db.nearest_halacha_for_vector(case_law_id, vecs[0])
        halacha_id = accept_match(best)
        if not halacha_id:
            continue
        treatment = await classify_treatment(c.get("citing_case_law_id") or c.get("citing_decision_id") or "", ctx)
        await db.store_corroboration(
            halacha_id, c["source_id"],
            c.get("citing_case_law_id"), c.get("citing_decision_id"),
            treatment, best[1], ctx,
        )
        linked += 1
    appr = await reconcile_approvals(case_law_id)
    return {"citations": len(cits), "linked": linked,
            "approved": appr["approved"], "demoted": appr["demoted"]}


async def reconcile_approvals(case_law_id: str | UUID) -> dict:
    """Apply the corroboration→approval policy to every halacha of a precedent
    (INV-COR2/COR4/COR5). No-op when the kill-switch is off. Idempotent: approve
    only fires on ``pending_review``, demote only on ``approved``, so re-runs
    converge."""
    if not config.HALACHA_CORROBORATION_AUTO_APPROVE:
        return {"approved": 0, "demoted": 0, "disabled": True}
    if isinstance(case_law_id, str):
        case_law_id = UUID(case_law_id)
    grouped = await db.list_corroboration_grouped(case_law_id)
    approved = demoted = 0
    for halacha_id, links in grouped.items():
        agg = aggregate(links)
        has_overruled = any(l["treatment"] == "overruled" for l in links)
        action = approval_action(agg, has_overruled)
        if action == "approve":
            if await db.approve_halacha_by_corroboration(
                UUID(halacha_id), agg["positive_sources"],
                config.HALACHA_CORROBORATION_MIN_CITES,
            ):
                approved += 1
        elif action == "demote":
            if await db.demote_halacha_overruled(UUID(halacha_id)):
                demoted += 1
    return {"approved": approved, "demoted": demoted, "disabled": False}


async def build_all() -> dict:
    """Backfill: build the signal + apply approvals for every precedent that has
    halachot and incoming citations. Idempotent (link table ``ON CONFLICT`` +
    state-gated transitions)."""
    ids = await db.precedents_with_halachot_and_incoming_citations()
    totals = {"precedents": 0, "citations": 0, "linked": 0,
              "approved": 0, "demoted": 0}
    for cid in ids:
        r = await build_for_precedent(cid)
        totals["precedents"] += 1
        for k in ("citations", "linked", "approved", "demoted"):
            totals[k] += r.get(k, 0)
        logger.info("corroboration backfill %s: %s", cid, r)
    return totals
