"""§2.3 — the lightweight ingress envelope (transport) and its deterministic map to a canonical VEB.

The ingress envelope is TRANSPORT: it carries the event plus transport metadata (request id, idempotency key,
receive time, the signature + identity method) and has no life of its own — it is NOT persisted as a canonical
object. `map_to_veb` deterministically projects ONLY the content-bearing fields into a `VebContentV1`; the
transport metadata is dropped, so it can never enter `content_hash` (canonical-hash purity, Appendix A.4).
Identity is verified through the one `IdentityVerifier` registry, selected by the declared `identity_method`.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional

from cl.identity.registry import VerifierRegistry
from cl.model.routing import RoutingOutcome, route
from cl.model.veb import BodyBinding, VebContentV1


@dataclass(frozen=True)
class IngressEnvelope:
    # --- content-bearing (projected into the canonical VEB) ---
    event_type: str
    actor_ref: str
    principal_ref: str
    hai5_level: int
    semantic_time: str
    body_binding: BodyBinding
    # --- transport-only (REQUIRED) — NEVER hashed (canonical-hash purity, A.4) ---
    ingress_request_id: str
    ingress_idempotency_key: str
    received_at: str
    # --- optional content ---
    schema_version: int = 1
    evidence_refs: tuple = ()
    relations: tuple = ()
    credential_ref: Optional[str] = None
    policy_snapshot_ref: Optional[str] = None
    # --- optional transport ---
    identity_method: Optional[str] = None
    signature: Optional[bytes] = None
    key_ref: Optional[str] = None


def map_to_veb(
    env: IngressEnvelope, hash_profile_id: bytes, ingress_assurance: Optional[str] = None
) -> VebContentV1:
    """Project the ingress envelope's CONTENT fields into a canonical `VebContentV1`. Transport metadata
    (request id, idempotency key, receive time, signature, identity method) is intentionally NOT carried —
    that is the canonical-hash purity boundary."""
    return VebContentV1(
        schema_id="veb",
        schema_version=env.schema_version,
        hash_profile_id=hash_profile_id,
        event_type=env.event_type,
        actor_ref=env.actor_ref,
        principal_ref=env.principal_ref,
        hai5_level=env.hai5_level,
        semantic_time=env.semantic_time,
        body_binding=env.body_binding,
        evidence_refs=tuple(env.evidence_refs),
        relations=tuple(env.relations),
        credential_ref=env.credential_ref,
        policy_snapshot_ref=env.policy_snapshot_ref,
        ingress_assurance=ingress_assurance,  # assigned by the DOOR, never carried by the envelope
    )


def process_ingress(
    env: IngressEnvelope,
    hash_profile_id: bytes,
    registry: Optional[VerifierRegistry] = None,
    ingress_assurance: Optional[str] = None,
) -> RoutingOutcome:
    """Map the ingress to a canonical VEB and route it (§2.2). If the envelope carries a signature, the
    matching verifier is selected from the registry by `identity_method`; an unknown method / failed
    verification REJECTs fail-closed, while an UNSIGNED envelope routes at the lowest assurance (A3)."""
    candidate = map_to_veb(env, hash_profile_id, ingress_assurance)
    verifier = registry.for_method(env.identity_method) if registry is not None else None
    return route(candidate, signature=env.signature, key_ref=env.key_ref, verifier=verifier)
