"""The ingest pipeline (§2.3 → §2.4): ingress envelope → route → append to the canonical journal.

`ingest` is the one entry point a transport adapter calls. It maps the transport envelope to a canonical VEB,
routes it (§2.2), and — only if accepted — appends a `StoredEvent` to the append-only store (idempotent by
`content_hash`). A REJECT is returned without touching the store. The stored `artifact_hash` binds the
verification profile reached for the event (A3 assurance), so the anchoring batch (§2.5) commits the right
artifact leaves.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional

from cl.canonical.jcs import canonical_bytes
from cl.ingress.envelope import IngressEnvelope, map_to_veb, process_ingress
from cl.identity.registry import VerifierRegistry
from cl.interfaces.storage import StorageBackend, StoredEvent
from cl.model import hashing
from cl.model.veb import PIPELINE_TRUSTED
from cl.model.routing import Decision, RoutingOutcome


def verification_profile_ref(outcome: RoutingOutcome) -> str:
    """The A3 verification profile reached for this event — bound into `artifact_hash`. An unsigned event is
    the lowest level, explicitly; a relying party reads the level from here, never higher."""
    return f"verify:{outcome.assurance.name.lower()}"


@dataclass(frozen=True)
class IngestResult:
    outcome: RoutingOutcome
    stored: Optional[StoredEvent]  # None on REJECT
    was_duplicate: bool = False    # True iff this content_hash was already in the store before append (additive)

    @property
    def accepted(self) -> bool:
        return self.outcome.accepted


def ingest(
    env: IngressEnvelope,
    store: StorageBackend,
    hash_profile_id: bytes,
    registry: Optional[VerifierRegistry] = None,
    ingress_assurance: str = PIPELINE_TRUSTED,
    extra_proofs: tuple = (),
) -> IngestResult:
    """Library-direct callers get PIPELINE_TRUSTED by default — the stronger guarantee (SEAM_GATED)
    is never assumed; only a gated door passes it explicitly (INGRESS_ASSURANCE_V1 spec §2.2)."""
    outcome = process_ingress(env, hash_profile_id, registry, ingress_assurance)
    if not outcome.accepted:
        return IngestResult(outcome, None)  # REJECT never touches the store

    veb = map_to_veb(env, hash_profile_id, ingress_assurance)
    vref = verification_profile_ref(outcome)
    proofs: tuple = tuple(extra_proofs)  # e.g. governance k-of-n co-signatures (verified upstream)
    if env.signature is not None:
        # The router has already verified this signature; it belongs to the artifact proof set, never the
        # semantic content hash. Persist the exact proof inputs so artifact_hash is independently reproducible.
        proofs = proofs + ({
            "identity_method": env.identity_method,
            "key_ref": env.key_ref,
            "signature": env.signature.hex(),
        },)
    proof_content = {"hash_profile_id": hash_profile_id.hex(), "proofs": list(proofs)}
    proof_hash = hashing.proof_set_hash(hash_profile_id, proofs)
    art = hashing.artifact_hash(hash_profile_id, outcome.content_hash, proof_hash, vref)
    event = StoredEvent(
        journal_offset=-1,  # assigned by the store
        content_hash=outcome.content_hash,
        artifact_hash=art,
        body_binding_kind="PAC_RO" if outcome.decision is Decision.PAC_RO else veb.body_binding.canonical()["kind"],
        canonical_bytes=canonical_bytes(veb.to_canonical_content()),
        proof_set_bytes=canonical_bytes(proof_content),
        verification_profile_ref=vref,
    )
    existed = store.get_by_content_hash(outcome.content_hash) is not None  # before append (additive, D)
    store.append(event)  # idempotent by content_hash
    return IngestResult(
        outcome,
        store.get_by_content_hash(outcome.content_hash) or event,
        was_duplicate=existed,
    )
