"""Independent device-session lifecycle contracts."""

from __future__ import annotations

from enum import StrEnum

from pydantic import AwareDatetime, Field, NonNegativeInt, model_validator

from .common import ContractModel, Identifier, LongText
from .identity import DeviceInstanceRef, ExecutionBinding


class EnrollmentState(StrEnum):
    EMPTY = "empty"
    SELECTING_MODEL = "selecting-model"
    SETUP_IN_PROGRESS = "setup-in-progress"
    ENROLLED = "enrolled"
    FAILED = "failed"


class ConnectivityState(StrEnum):
    UNKNOWN = "unknown"
    OFFLINE = "offline"
    CONNECTING = "connecting"
    CONNECTED = "connected"
    DEGRADED = "degraded"


class AcquisitionState(StrEnum):
    IDLE = "idle"
    PREPARING = "preparing"
    STARTING = "starting"
    STREAMING = "streaming"
    STOPPING = "stopping"
    FAILED = "failed"


class DeviceSessionContext(ContractModel):
    """Immutable identity and binding established when a plugin opens a session."""

    session_id: Identifier
    device: DeviceInstanceRef
    execution: ExecutionBinding
    compatibility_profile_id: Identifier | None = None
    compatibility_assessment_id: Identifier | None = None
    opened_at: AwareDatetime


class DeviceSessionRef(ContractModel):
    session_id: Identifier
    device_id: Identifier


class DeviceSessionSnapshot(ContractModel):
    """Revisioned state; enrollment, connectivity and acquisition never alias."""

    context: DeviceSessionContext
    revision: NonNegativeInt
    enrollment: EnrollmentState
    connectivity: ConnectivityState
    acquisition: AcquisitionState
    observed_at: AwareDatetime
    message: LongText | None = None
    active_operation_ids: tuple[Identifier, ...] = Field(default_factory=tuple)

    @model_validator(mode="after")
    def operation_ids_must_be_unique(self) -> DeviceSessionSnapshot:
        if len(self.active_operation_ids) != len(set(self.active_operation_ids)):
            raise ValueError("active operation ids must be unique")
        return self
