"""Mark a SelfModel falsified or stale.

Falsification semantics (per ticket #000014 §2.3):

- ``falsified`` — a cited capability claim re-evaluated and dropped
  below threshold (with bench-floor 5pp tolerance per
  ``docs/bench-maxing.md``). Strong signal — operators should not
  rely on this SelfModel for new claims.
- ``stale`` — upstream change (verifier method, governance policy,
  patch root) means the SelfModel no longer reflects current state
  even though no specific claim was falsified. Soft signal — new
  queries should prefer a fresher SelfModel.

Both transitions emit audit events:

- ``selfmodel_falsified`` carries the claim_hash that triggered.
- ``selfmodel_marked_stale`` carries the upstream change reason.
"""

from __future__ import annotations

import sqlite3
import time
from typing import Optional

from arborist.store import append_audit


def falsify(
    conn: sqlite3.Connection,
    selfmodel_root: str,
    *,
    reason: str,
    triggering_claim_hash: Optional[str] = None,
    ts: Optional[int] = None,
) -> str:
    """Flip a SelfModel's state to ``falsified``; return audit event_hash.

    Idempotent: if state is already ``falsified``, no-op (no new event).
    Cannot un-falsify — the only path forward is a new SelfModel
    snapshot.
    """
    row = conn.execute(
        "SELECT state FROM selfmodel_records WHERE selfmodel_root = ?",
        (selfmodel_root,),
    ).fetchone()
    if row is None:
        raise KeyError(f"selfmodel_root not found: {selfmodel_root}")
    if row["state"] == "falsified":
        return ""

    if ts is None:
        ts = int(time.time())

    event_hash = append_audit(
        conn,
        event_type="selfmodel_falsified",
        subject_root=selfmodel_root,
        body={
            "selfmodel_root": selfmodel_root,
            "reason": reason,
            "triggering_claim_hash": triggering_claim_hash,
        },
        ts=ts,
    )
    conn.execute(
        "UPDATE selfmodel_records "
        "SET state = 'falsified', falsified_at = ?, falsified_reason = ? "
        "WHERE selfmodel_root = ?",
        (ts, reason, selfmodel_root),
    )
    return event_hash


def mark_stale(
    conn: sqlite3.Connection,
    selfmodel_root: str,
    *,
    reason: str,
    ts: Optional[int] = None,
) -> str:
    """Flip a SelfModel's state to ``stale``; return audit event_hash.

    Idempotent on ``stale`` and ``falsified`` (terminal states; no-op).
    Stale is only valid as a one-way transition from ``live``.
    """
    row = conn.execute(
        "SELECT state FROM selfmodel_records WHERE selfmodel_root = ?",
        (selfmodel_root,),
    ).fetchone()
    if row is None:
        raise KeyError(f"selfmodel_root not found: {selfmodel_root}")
    if row["state"] in ("stale", "falsified"):
        return ""

    if ts is None:
        ts = int(time.time())

    event_hash = append_audit(
        conn,
        event_type="selfmodel_marked_stale",
        subject_root=selfmodel_root,
        body={
            "selfmodel_root": selfmodel_root,
            "reason": reason,
        },
        ts=ts,
    )
    conn.execute(
        "UPDATE selfmodel_records "
        "SET state = 'stale' "
        "WHERE selfmodel_root = ?",
        (selfmodel_root,),
    )
    return event_hash
