"""Devices domain - SQLAlchemy models.

A ``Device`` row is created ``pending_pairing`` at ``POST /api/devices/pair/start`` and stays
unowned (no ``user_id``) until a signed-in user approves it. Devices are user-owned;
``last_workspace_id`` is UI context only. Pairing fields (``device_code``, ``user_code``,
``pairing_expires_at``) are cleared once the durable ``device_secret`` has been delivered.
"""
from __future__ import annotations
import enum
from datetime import datetime
from typing import Optional
from uuid import UUID, uuid4

from sqlalchemy import ForeignKey, String
from sqlalchemy.dialects.postgresql import JSONB, UUID as PGUUID
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql import func
from sqlalchemy.types import DateTime

from src.core.database import Base


class DeviceStatus(str, enum.Enum):
    pending_pairing = "pending_pairing"
    active = "active"
    offline = "offline"
    unhealthy = "unhealthy"
    revoked = "revoked"


class DeviceEventType(str, enum.Enum):
    paired = "paired"
    revoked = "revoked"
    health_failed = "health_failed"
    recovered = "recovered"


class Device(Base):
    __tablename__ = "devices"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)

    # Unset until pairing is approved. last_workspace_id is UI context only — not ownership.
    last_workspace_id: Mapped[Optional[UUID]] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("workspaces.id", ondelete="SET NULL"), nullable=True
    )
    user_id: Mapped[Optional[UUID]] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )

    name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
    hostname: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    os: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)  # windows | macos | linux
    os_version: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
    app_version: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)

    device_secret_hash: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)

    status: Mapped[str] = mapped_column(
        String(20), nullable=False, default=DeviceStatus.pending_pairing.value,
        server_default=DeviceStatus.pending_pairing.value,
    )
    capabilities: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)

    last_heartbeat_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    last_seen_ip: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)

    paired_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    revoked_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    revoked_by: Mapped[Optional[UUID]] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("users.id"), nullable=True
    )

    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    # --- Pairing (OAuth device-code style) ---
    # device_code: long opaque secret only the desktop app and Core know; used for poll + approve.
    # user_code: short human-readable code shown to the user for confirmation in Dash.
    # Both are nulled out once the device_secret has been delivered (see deliver_secret).
    device_code: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, unique=True)
    user_code: Mapped[Optional[str]] = mapped_column(String(16), nullable=True)
    pairing_expires_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)


class DeviceEvent(Base):
    """Audit log for device lifecycle events — mirrors WorkerFleetEvent."""

    __tablename__ = "device_events"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    device_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("devices.id", ondelete="CASCADE"), nullable=False
    )
    event_type: Mapped[str] = mapped_column(String(32), nullable=False)
    # user | admin | health_monitor | security_event
    triggered_by: Mapped[str] = mapped_column(String(32), nullable=False, default="user")
    occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    metadata_json: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
