"""Versioned contracts shared by cards, backends, the API, and receipts."""

from __future__ import annotations

from datetime import datetime, timezone
from enum import StrEnum
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field, field_validator


class Lane(StrEnum):
    RESIDUAL = "RESIDUAL"
    MEMORY = "MEMORY"
    ADAPTER = "ADAPTER"
    CONTROL = "CONTROL"
    WORLD = "WORLD"
    VERIFY = "VERIFY"
    EVENT = "EVENT"
    OUTPUT = "OUTPUT"


CardKind = Literal[
    "policy",
    "memory",
    "tool",
    "steering",
    "adapter",
    "sensor",
    "vocabulary",
]


class ResourceBudget(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)

    max_delta_norm: float = Field(default=0.08, ge=0.0, le=1.0)
    max_latency_ms: int = Field(default=250, ge=1, le=60_000)
    max_state_bytes: int = Field(default=1_048_576, ge=0, le=1_073_741_824)


class CardManifest(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)

    schema_version: Literal["1.0"] = "1.0"
    id: str = Field(pattern=r"^[a-z][a-z0-9.-]{2,63}$")
    name: str = Field(min_length=1, max_length=80)
    version: str = Field(pattern=r"^[0-9]+\.[0-9]+\.[0-9]+(?:[-+][A-Za-z0-9.-]+)?$")
    publisher: str = Field(default="Cyberelf Labs", min_length=1, max_length=120)
    kind: CardKind
    reads: tuple[Lane, ...] = ()
    writes: tuple[Lane, ...] = ()
    phases: tuple[Literal["before", "activation", "after"], ...] = ("before",)
    capabilities: tuple[str, ...] = ()
    compatible_models: tuple[str, ...] = ("*",)
    state_scope: Literal["request", "tenant", "persistent"] = "request"
    priority: int = Field(default=50, ge=0, le=100)
    budget: ResourceBudget = ResourceBudget()

    @field_validator("reads", "writes", "phases", "capabilities", "compatible_models")
    @classmethod
    def no_duplicates(cls, value: tuple[Any, ...]) -> tuple[Any, ...]:
        if len(value) != len(set(value)):
            raise ValueError("duplicate manifest entries are not allowed")
        return value


class CardPackage(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)

    manifest: CardManifest
    config: dict[str, Any] = Field(default_factory=dict)
    digest_sha256: str | None = Field(default=None, pattern=r"^[a-f0-9]{64}$")
    signer_fingerprint: str | None = Field(default=None, pattern=r"^[a-f0-9]{64}$")
    source: Literal["signed-package", "builtin"] = "signed-package"


class GenerationRequest(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)

    tenant_id: str = Field(pattern=r"^[a-z][a-z0-9-]{1,63}$")
    prompt: str = Field(min_length=1, max_length=32_000)
    max_new_tokens: int = Field(default=96, ge=1, le=512)
    request_id: str | None = Field(default=None, max_length=128)
    metadata: dict[str, str] = Field(default_factory=dict)


class BackendCapabilities(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)

    backend: str
    model: str
    batching: bool
    kv_cache: bool
    activation_cards: bool
    per_request_adapters: bool
    metrics: tuple[str, ...] = ()
    limitations: tuple[str, ...] = ()


class TokenUsage(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)

    input_tokens: int = 0
    output_tokens: int = 0


class ExecutionReceipt(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)

    schema_version: Literal["1.0"] = "1.0"
    receipt_id: str
    request_id: str
    tenant_id: str
    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
    status: Literal["completed", "blocked", "failed"]
    backend: str
    model: str
    card_stack: tuple[str, ...]
    stack_digest_sha256: str
    request_digest_sha256: str
    response_digest_sha256: str
    latency_ms: float
    queue_ms: float
    batch_size: int
    token_usage: TokenUsage
    gpu_memory_bytes: int | None = None
    events: tuple[dict[str, Any], ...] = ()
    limitation_notes: tuple[str, ...] = ()


class RuntimeResult(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)

    text: str
    receipt: ExecutionReceipt

