"""Verification flag types with evidence.

Three axes — no cross-contamination:

  Epistemic failure (ungrounded assertions)
    Model asserts world-claims without support in PEF or provided context.
    Default governance outcome: FORCE_REVISE (cite, hedge, or admit ignorance).

  PEF binding failures
    Referent resolution and span-consistency failures against the world model.

  Content-class veto (normative refusal)
    Prohibited categories regardless of truth value.
    "Even if true, you don't get to say it."
    Default governance outcome: REFUSE immediately — no revise loop.
"""

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum, auto


class FlagType(Enum):
    # ── Epistemic failure (ungrounded assertions) ─────────────────────────────
    UNBOUND_ENTITY = auto()                 # Claim subject has no PEF binding; facts about unbound entity
    UNSUPPORTED_ATTRIBUTE = auto()          # Attribute asserted for known entity without PEF support
    UNSUPPORTED_EVENT = auto()              # Event/relationship asserted without PEF support
    UNVERIFIED_FACT_ASSERTION = auto()      # Temporal/quantitative fact with no PEF/context support
    UNVERIFIED_REGULATORY_CLAIM = auto()    # FDA/EMA/CE/WHO approval asserted without verifiable support

    # ── PEF binding failures ─────────────────────────────────────────────────
    UNRESOLVED_REFERENT = auto()            # Common-noun referent not bound into PEF (e.g. "sister")
    UNRESOLVED_COMPARAND = auto()           # Comparative adjective with 2+ eligible comparands in PEF
    IDENTITY_DRIFT = auto()                 # Entity properties changed without cause
    TIME_SMEAR = auto()                     # Past/present context mixed without span transition
    CONTRADICTED_FACT = auto()              # Directly contradicts an established PEF relationship
    UNRESOLVED_STATE_TRANSITION = auto()    # Treats an unresolved state as settled fact

    # ── Extraction failures ──────────────────────────────────────────────────
    EXTRACTION_FAILED = auto()              # Backend returned invalid/unparseable output
    EXTRACTION_EMPTY = "extraction_empty"   # Schema-valid but barren (no claims, no entity_mentions)

    # ── Content-class veto — hard-stop (all modes) ───────────────────────────
    # These are never allowed from a language model regardless of claimed support.
    # Governance outcome: HARD_STOP, no revise loop.
    MEDICAL_DOSAGE_RECOMMENDATION = "medical_dosage"           # Explicit patient dosing instruction
    PEDIATRIC_DOSAGE_RECOMMENDATION = "pediatric_dosage"       # Pediatric-specific dosage
    NUMERIC_MEDICAL_INSTRUCTION = "numeric_medical_instruction" # Titration/infusion/taper/insulin schedules
    EMERGENCY_TRIAGE_GUIDANCE = "emergency_triage"             # Definitive triage or "don't seek care" reassurance
    SELF_HARM_INSTRUCTION = "self_harm"                        # Instructions facilitating self-harm
    ILLEGAL_INSTRUCTION = "illegal_instruction"                # Weapons, fraud, hacking, evading law enforcement
    TARGETED_DEFAMATION = "targeted_defamation"                # Factual allegations about private individuals
    VIOLENT_CRIMINAL_INTENT = "violent_criminal_intent"        # Interpersonal homicide framed as legal/permissibility query

    # ── Content-class veto — verify-or-refuse (mode-dependent) ───────────────
    # HARD_STOP in public mode.
    # FORCE_REVISE in enterprise mode (allowed only when grounded in verified sources).
    # Mode is set per-deployment in config — never inferred from request text.
    PERSONALIZED_MEDICAL_ADVICE = "personalized_medical"       # Individualized diagnosis / treatment plan
    PERSONALIZED_LEGAL_ADVICE = "personalized_legal"           # Jurisdictional procedure / deadline advice
    PERSONALIZED_FINANCIAL_ADVICE = "personalized_financial"   # Individualized investment / tax advice
    SENSITIVE_PII_EXPOSURE = "sensitive_pii"                   # Outputting or inferring private personal data
    UNVERIFIED_TRUTHFULNESS_CLAIM = "unverified_truthfulness"  # Definitive assertion about private individual's honesty/deception


@dataclass
class Flag:
    """A verification flag raised against an LLM response claim."""
    flag_type: FlagType
    entity_name: str                   # Entity involved
    claim: str                         # What the LLM said
    evidence: str                      # Why this was flagged
    severity: str = "warning"          # "warning" or "error"
    extraction_diagnostic: dict | None = None  # For EXTRACTION_FAILED: provider, model, prompt_hash
    candidates: tuple[str, ...] = ()   # Normalized PEF labels for ambiguity flags
                                       # (UNRESOLVED_REFERENT, UNRESOLVED_COMPARAND).
                                       # Entity names from the world model, never raw evidence.
                                       # These are entity names from the world model, never
                                       # raw evidence text. Renderers may surface these to
                                       # the user. All other flag fields are audit-only.

    def __str__(self) -> str:
        return f"[{self.flag_type.name}] {self.entity_name}: {self.claim} — {self.evidence}"


def flag_from_external(raw: dict) -> Flag | None:
    """Convert external flag dict to Flag. Returns None if type unknown.

    Expected format: {"type": "SELF_HARM_INSTRUCTION", "evidence": ["span"], "source": "..."}
    """
    type_str = raw.get("type")
    if not type_str or not isinstance(type_str, str):
        return None
    try:
        flag_type = FlagType[type_str]
    except KeyError:
        return None
    evidence = raw.get("evidence")
    if isinstance(evidence, list):
        evidence_str = "; ".join(str(s) for s in evidence[:3])
    else:
        evidence_str = str(evidence) if evidence else "external"
    source = raw.get("source", "external")
    return Flag(
        flag_type=flag_type,
        entity_name="external",
        claim=f"External flag from {source}",
        evidence=evidence_str or source,
        severity=raw.get("severity", "warning"),
    )
