"""Command queue model - pending commands for execution."""

from datetime import datetime
from enum import Enum as PyEnum

from sqlalchemy import CheckConstraint, Column, DateTime, Index, Integer, Text, text
from sqlalchemy.dialects.postgresql import JSONB, UUID

from iot_db.models.base import Base


class CommandStatus(PyEnum):
    """Status of a command in the queue."""

    pending = "pending"
    processing = "processing"
    completed = "completed"
    failed = "failed"
    cancelled = "cancelled"


class CommandPlatform(PyEnum):
    """Supported IoT platforms for command execution."""

    supla = "supla"
    chirpstack = "chirpstack"


class CommandQueue(Base):
    """Queue of commands pending execution.

    This table serves as the central queue for all device control commands.
    Workers poll this table to pick up pending commands and execute them
    against the appropriate IoT platform (Supla, ChirpStack).

    Key features:
    - Priority-based ordering for command execution
    - Retry mechanism with exponential backoff
    - Locking to prevent duplicate processing by multiple workers
    - Full audit trail via related CommandLog entries

    Example record:
        id: <uuid>
        tenant: <tenant_uuid>
        device_id: "supla_device_12345_ch1"
        platform: "supla"
        platform_device_id: "4702"
        action: "turn_off"
        params: {}
        status: "pending"
        priority: 0
        attempt_count: 0
        max_attempts: 3
        next_retry_at: None
        created_by: "user:123"
        created_at: 2026-02-23T10:00:00Z
        updated_at: 2026-02-23T10:00:00Z
        locked_by: None
        locked_at: None

    Attributes:
        id: Primary key (UUID)
        tenant: Tenant UUID (multi-tenant support)

        # Device identification
        device_id: meter_id from dim_device
        platform: Target platform ("supla" or "chirpstack")
        platform_device_id: Platform-specific device ID (channel_id for Supla, devEui for ChirpStack)

        # Command specification
        action: Command action (turn_on, turn_off, set_value, set_config, etc.)
        params: JSON parameters for the action (e.g., {"value": 22.5})

        # Status and priority
        status: Current command status (pending, processing, completed, failed, cancelled)
        priority: Higher values = higher priority (default: 0)

        # Retry mechanism
        attempt_count: Number of execution attempts made
        max_attempts: Maximum retry attempts before marking as failed
        next_retry_at: Scheduled time for next retry attempt

        # Audit fields
        created_by: Command initiator ("user:{user_id}", "rule:{rule_id}", "api:{token_id}")
        created_at: Command creation timestamp
        updated_at: Last update timestamp

        # Processing lock
        locked_by: Worker ID that has locked this command
        locked_at: Timestamp when the lock was acquired
    """

    __tablename__ = "command_queue"

    # Primary key
    id = Column(
        UUID(as_uuid=True),
        primary_key=True,
        server_default=text("gen_random_uuid()"),
    )

    # Multi-tenant support
    tenant = Column(UUID(as_uuid=True), nullable=False, index=True)

    # Device identification
    device_id = Column(Text, nullable=False, index=True)
    platform = Column(Text, nullable=False)
    platform_device_id = Column(Text, nullable=False)

    # Command specification
    action = Column(Text, nullable=False)
    params = Column(JSONB, server_default="{}", nullable=False)

    # Status and priority
    status = Column(Text, nullable=False, server_default="pending")
    priority = Column(Integer, server_default="0", nullable=False)

    # Retry mechanism
    attempt_count = Column(Integer, server_default="0", nullable=False)
    max_attempts = Column(Integer, server_default="3", nullable=False)
    next_retry_at = Column(DateTime(timezone=True), nullable=True)

    # Audit fields
    created_by = Column(Text, nullable=False)
    created_at = Column(
        DateTime(timezone=True),
        nullable=False,
        server_default="NOW()",
    )
    updated_at = Column(
        DateTime(timezone=True),
        nullable=False,
        server_default="NOW()",
        onupdate=datetime.utcnow,
    )

    # Processing lock
    locked_by = Column(Text, nullable=True)
    locked_at = Column(DateTime(timezone=True), nullable=True)

    __table_args__ = (
        # Constraints
        CheckConstraint(
            "status IN ('pending', 'processing', 'completed', 'failed', 'cancelled')",
            name="valid_status",
        ),
        CheckConstraint(
            "platform IN ('supla', 'chirpstack')",
            name="valid_platform",
        ),
        # Index for efficient pending command retrieval
        Index(
            "idx_command_queue_pending",
            "priority",
            "created_at",
            postgresql_where="status = 'pending'",
        ),
        # Indexes for common queries
        Index("idx_command_queue_tenant", "tenant", "created_at"),
        Index("idx_command_queue_device", "device_id", "created_at"),
        Index(
            "idx_command_queue_locked",
            "locked_at",
            postgresql_where="locked_by IS NOT NULL",
        ),
    )

    def __repr__(self):
        return (
            f"<CommandQueue(id={self.id}, device_id={self.device_id}, "
            f"action={self.action}, status={self.status})>"
        )

    def is_retriable(self) -> bool:
        """Check if the command can be retried."""
        return self.attempt_count < self.max_attempts

    def is_locked(self) -> bool:
        """Check if the command is currently locked for processing."""
        return self.locked_by is not None
