"""B — `ActionIntentV1`: the PRE-EXECUTION request object (Design Note §B).

An ActionIntent is what a controlled-execution agent emits BEFORE it acts: it names the operation, the actor /
principal / tenant, a commitment to the inputs, and the authority-context manifest hash it was decided under.
It is content-addressed through the A1 primitive (the `hash_profile_id` is folded INTO the hashed content),
so a downstream FULFILLMENT / EXCEPTION can reference an intent by a hash that is inseparable from its
algorithm. Optional fields are OMITTED when absent for a stable canonical form (mirrors VEB/PAC-RO).
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Optional

from cl.canonical.hash_profile import content_hash

DOMAIN_ACTION_INTENT = "COTRUGLI:ACTION_INTENT:CONTENT:V1"


@dataclass(frozen=True)
class ActionIntentV1:
    schema_id: str
    schema_version: int
    hash_profile_id: bytes
    operation: str
    actor_ref: str
    principal_ref: str
    tenant: str
    input_commitment: str                       # hex commitment over the (off-content) inputs
    authority_context_manifest_hash: str        # hex — the scoped/signed context this intent was raised under
    semantic_time: str
    policy_snapshot_ref: Optional[str] = None
    valid_until: Optional[str] = None

    def to_content(self) -> dict:
        """The canonical content dict that enters `content_hash` — `hash_profile_id` INSIDE (A1), optional
        fields OMITTED when absent (stable canonical form)."""
        content: dict[str, Any] = {
            "schema_id": self.schema_id,
            "schema_version": self.schema_version,
            "hash_profile_id": self.hash_profile_id.hex(),  # A1: the id is part of the hashed content
            "operation": self.operation,
            "actor_ref": self.actor_ref,
            "principal_ref": self.principal_ref,
            "tenant": self.tenant,
            "input_commitment": self.input_commitment,
            "authority_context_manifest_hash": self.authority_context_manifest_hash,
            "semantic_time": self.semantic_time,
        }
        if self.policy_snapshot_ref is not None:
            content["policy_snapshot_ref"] = self.policy_snapshot_ref
        if self.valid_until is not None:
            content["valid_until"] = self.valid_until
        return content

    def action_intent_hash(self) -> bytes:
        """A1: `HASH_BY_PROFILE(id, COTRUGLI:ACTION_INTENT:CONTENT:V1 ‖ canonical(content INCLUDING id))`."""
        return content_hash(self.hash_profile_id, DOMAIN_ACTION_INTENT, self.to_content())
