"""Frozen dataclasses for every Formation Pipeline artifact.

These are pure data carriers — no behavior, no IO.  Each carries a
``schema_version`` so future migrations can detect mismatch at load time.

Serialization is via canonical JSON (``formation.hashing.canonical_json``).
SHA-256 of the canonical form is the content address.

Trust regions, left to right:
    OreBundle           (UNTRUSTED)
    ValidatedTripleSet  (REVIEWED — passed the Forge)
    CourseYAML          (REVIEWED — composed from validated triples)
    FormationPlan       (REVIEWED — compiled from CourseYAML)
    MasteryReport       (RATIFIED — self-sealed; only this authorizes promote)
"""

from __future__ import annotations

from dataclasses import dataclass, field

from formation.candidate import (
    ConceptCandidate,
    CounterCandidate,
    OrderingHint,
    RelationCandidate,
)

SCHEMA_VERSION: str = "1.0.0"


@dataclass(frozen=True, slots=True)
class SubjectSpec:
    """The one human-authored artifact in the chain.

    Generated by ``core formation new <subject_id>`` as a YAML stub; edited
    by the user; loaded into this dataclass for everything downstream.
    """

    subject_id: str
    title: str
    target_depth: str  # e.g. "introductory", "working", "expert"
    requires_courses: tuple[str, ...] = ()
    anti_requisites: tuple[str, ...] = ()
    identity_axis_constraints: tuple[str, ...] = ()
    schema_version: str = SCHEMA_VERSION


@dataclass(frozen=True, slots=True)
class OreEntry:
    """A single mined source: URL, bytes-SHA, retrieval metadata."""

    source_sha: str
    url: str
    adapter: str
    retrieved_at: str  # ISO-8601 UTC
    byte_length: int
    citation: str = ""


@dataclass(frozen=True, slots=True)
class OreBundle:
    """The output of Stage 1 (Mining).  Untrusted material."""

    subject_id: str
    entries: tuple[OreEntry, ...]
    schema_version: str = SCHEMA_VERSION


@dataclass(frozen=True, slots=True)
class ValidatedTripleSet:
    """The output of Stage 3 (Forge).  First artifact the rest of CORE can lawfully see.

    Every entry here has passed every Forge rule and carries
    ``EpistemicStatus.SPECULATIVE`` once handed to the teaching layer.
    """

    subject_id: str
    concepts: tuple[ConceptCandidate, ...]
    relations: tuple[RelationCandidate, ...]
    counters: tuple[CounterCandidate, ...]
    ordering_hints: tuple[OrderingHint, ...]
    quarantined: tuple[RelationCandidate, ...] = ()
    schema_version: str = SCHEMA_VERSION


@dataclass(frozen=True, slots=True)
class CourseYAML:
    """The output of Stage 4 (Compose).  Deterministic, byte-stable."""

    course_id: str
    yaml_bytes: bytes
    course_sha256: str
    source_bundle_sha: str
    validated_set_sha: str
    template_id: str
    template_version: str
    schema_version: str = SCHEMA_VERSION


@dataclass(frozen=True, slots=True)
class PlanStep:
    """One step of a FormationPlan.

    ``step_type`` is one of:
        "seed_concept", "introduce_relation", "walk_step",
        "adversarial_probe", "replay_assertion".
    ``payload`` is a JSON-serializable dict; floats are forbidden.
    """

    step_type: str
    payload: dict[str, object]


@dataclass(frozen=True, slots=True)
class FormationPlan:
    """The output of Stage 5 (Compile).  Deterministic given the same CourseYAML."""

    course_id: str
    course_sha256: str
    steps: tuple[PlanStep, ...]
    plan_sha256: str
    schema_version: str = SCHEMA_VERSION


@dataclass(frozen=True, slots=True)
class GateMeasurement:
    """One gate's outcome at ratification time.

    Numeric values are stringified to keep canonical JSON deterministic.
    """

    name: str
    passed: bool
    measurement: str  # stringified numeric or descriptor
    threshold: str = ""


@dataclass(frozen=True, slots=True)
class MasteryReport:
    """The output of Stage 7 (Ratify).  Self-sealed cryptographic receipt.

    ``report_sha256`` is the SHA over the JSON body with ``report_sha256``
    blanked.  See ``formation.hashing.self_seal`` / ``verify_seal``.
    """

    course_id: str
    source_bundle_sha: str
    validated_set_sha: str
    course_sha256: str
    plan_sha256: str
    gates: tuple[GateMeasurement, ...]
    trace_hashes: tuple[str, ...]
    ratified: bool
    report_sha256: str = ""
    schema_version: str = SCHEMA_VERSION
    issued_at: str = ""  # ISO-8601 UTC, set at emission

    # Failure reasons accumulate when ``ratified`` is False; empty otherwise.
    failure_reasons: tuple[str, ...] = field(default_factory=tuple)
