"""Stable device identity contracts.

Transport-local names (for example a CoreBluetooth UUID assigned by one Mac)
may be recorded as aliases, but cannot be asserted as a stable device identity.
"""

from __future__ import annotations

from enum import StrEnum

from pydantic import AwareDatetime, Field, model_validator

from .common import ContractModel, Identifier, ShortText


class IdentityStability(StrEnum):
    STABLE = "stable"
    PROVISIONAL = "provisional"


class IdentityBasis(StrEnum):
    HARDWARE_IDENTIFIER = "hardware-identifier"
    PLUGIN_DERIVED = "plugin-derived"
    OPERATOR_ASSIGNED = "operator-assigned"
    TRANSPORT_LOCAL = "transport-local"


class ExecutionPlatform(StrEnum):
    MACOS = "macos"
    LINUX = "linux"
    WINDOWS = "windows"
    UNKNOWN = "unknown"


class DeviceModelRef(ContractModel):
    plugin_id: Identifier
    plugin_version: ShortText
    model_id: Identifier


class TransportAlias(ContractModel):
    """A non-authoritative address observed in one transport scope."""

    transport: Identifier
    scope_id: Identifier
    value: ShortText
    observed_at: AwareDatetime


class DeviceInstanceRef(ContractModel):
    """Plugin-scoped device identity, independent from a connection session."""

    device_id: Identifier
    model: DeviceModelRef
    stability: IdentityStability
    basis: IdentityBasis
    aliases: tuple[TransportAlias, ...] = Field(default_factory=tuple)

    @model_validator(mode="after")
    def stable_identity_must_not_be_transport_local(self) -> DeviceInstanceRef:
        if (
            self.stability is IdentityStability.STABLE
            and self.basis is IdentityBasis.TRANSPORT_LOCAL
        ):
            raise ValueError("a transport-local identifier cannot be a stable device identity")
        return self


class ExecutionBinding(ContractModel):
    """The logical node and concrete agent process hosting a device session."""

    node_id: Identifier
    agent_instance_id: Identifier
    platform: ExecutionPlatform
