"""Master Capability Registry v0 — Compliance Execution domain (Phase 2C).

Built from the Reasoning session per user directive, but this IS the Compliance
Execution model (Execution owns Capability). Third real instance of the
identity-machine pattern (after Master Controls and Master Obligations):

    Candidate -> Normalization -> Dedup -> Stable Identity (MCAP) -> Typed Relations

KEY SENTENCE (stored vs derived):
    STORED  : identities, sources, relationship types, policy versions, lifecycle
              events, provenance.
    DERIVED : confidence, coverage and gap statements — computed on demand, NEVER
              stored (see policy.py / engine.evaluate_relation).

These are APPLICATION/registry types, NOT compliance-meta-model classes. In
particular `CapabilityRelation` is relation METADATA inside the registry — it does
NOT introduce a new meta-model class. Whether a reified relation must enter the
frozen meta-model is a Meta-Model-Owner decision (architecture freeze v1.0),
deferred until a demonstrable failure case exists.

Self-contained (no Reasoning import — Reasoning consumes Capability, not the other
way round). Python 3.9 compatible (no `|` unions).
"""

from __future__ import annotations

from enum import Enum
from typing import List, Optional

from pydantic import BaseModel, Field


class Confidence(str, Enum):
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class AssertionStatus(str, Enum):
    """How well-established a capability claim is. A numeric score is presentation;
    THIS type is the truth (derived from relationship type + evidence + policy)."""

    DECLARED = "declared"
    INFERRED = "inferred"
    CONFIRMED = "confirmed"
    UNKNOWN = "unknown"


class RelationType(str, Enum):
    EQUIVALENT = "equivalent"
    SUPPORTS = "supports"
    REQUIRES = "requires"
    CONFIRMS = "confirms"
    BROADER_THAN = "broader_than"
    NARROWER_THAN = "narrower_than"
    RELATED_TO = "related_to"


class EvidenceKind(str, Enum):
    CERTIFICATION = "certification"  # a held certificate — a CLAIM, never proof
    ARTIFACT = "artifact"  # concrete doc/config/test/log
    EXPERT = "expert"  # human expert assertion
    NONE = "none"


class LifecycleState(str, Enum):
    ACTIVE = "active"
    DEPRECATED = "deprecated"


class LifecycleEventType(str, Enum):
    MERGE = "merge"
    SPLIT = "split"
    DEPRECATE = "deprecate"
    REDIRECT = "redirect"


class Provenance(BaseModel):
    """Every CURATED atom carries its own provenance (who / when / on what basis)."""

    author: str = ""
    asserted_at: Optional[str] = None  # ISO timestamp passed in; never generated here
    basis: str = ""


# ── stored: identity ──────────────────────────────────────────────────────
class MasterCapability(BaseModel):
    capability_id: str  # stable MCAP-xxxxx
    name: str = ""
    definition: str = ""
    category: str = ""
    domains: List[str] = Field(default_factory=list)
    typical_evidence: List[str] = Field(default_factory=list)
    version: int = 1
    state: LifecycleState = LifecycleState.ACTIVE
    redirect_to: Optional[str] = None  # set on merge/deprecate
    provenance: Provenance = Field(default_factory=Provenance)


class CapabilityCandidate(BaseModel):
    raw_term: str  # e.g. "Patch Management"
    source: str = ""  # e.g. "CRA:Annex I (2)(d)"
    normalized: str = ""


# ── stored: typed relation metadata (NOT a meta-model class) ──────────────
class CapabilityRelation(BaseModel):
    relation_id: str
    source: str  # external term/obligation/certification id, e.g. "certification:ISO27001"
    target_capability_id: str  # MCAP-...
    relationship_type: RelationType
    evidence_kind: EvidenceKind = EvidenceKind.NONE
    provenance: Provenance = Field(default_factory=Provenance)


# ── stored: versioned derivation policy ───────────────────────────────────
class PolicyRule(BaseModel):
    relationship_type: RelationType
    evidence_kind: EvidenceKind
    status: AssertionStatus
    confidence: Confidence


class PolicyVersion(BaseModel):
    """A versioned derivation policy. `policy_version` is recorded with every
    assessment so "why did you say X last year" is answerable with the policy
    as-of-then. Without this, `derived` and `auditable/reproducible` contradict."""

    policy_version: str
    description: str = ""
    rules: List[PolicyRule] = Field(default_factory=list)


# ── stored: identity lifecycle ────────────────────────────────────────────
class IdentityLifecycleEvent(BaseModel):
    event_id: str
    event_type: LifecycleEventType
    from_ids: List[str] = Field(default_factory=list)
    to_ids: List[str] = Field(default_factory=list)
    at: Optional[str] = None
    provenance: Provenance = Field(default_factory=Provenance)


# ── DERIVED — never stored ────────────────────────────────────────────────
class DerivedAssessment(BaseModel):
    target_capability_id: str
    status: AssertionStatus
    confidence: Confidence
    policy_version: str
    explanation: str = ""
