"""
Data models for laser control module.

Provides dataclasses and enums for structured data representation
throughout the laser control system.
"""

from dataclasses import dataclass
from enum import IntEnum
from typing import Optional, Dict, Any
from datetime import datetime


class VariationType(IntEnum):
    """Types of parameter variation modes."""
    MANUAL = 0x00
    CHANGE_CURRENT_LD1 = 0x01
    CHANGE_CURRENT_LD2 = 0x02
    CHANGE_TEMPERATURE_LD1 = 0x03
    CHANGE_TEMPERATURE_LD2 = 0x04


class DeviceState(IntEnum):
    """Device operational states."""
    IDLE = 0x0000
    RUNNING = 0x0001
    BUSY = 0x0002
    ERROR = 0x00FF
    ERROR_OVERHEAT = 0x0100
    ERROR_POWER = 0x0200
    ERROR_COMMUNICATION = 0x0400
    ERROR_INVALID_COMMAND = 0x0800


@dataclass
class ManualModeParams:
    """Parameters for manual control mode."""
    temp1: float  # Temperature for laser 1 (°C)
    temp2: float  # Temperature for laser 2 (°C)
    current1: float  # Current for laser 1 (mA)
    current2: float  # Current for laser 2 (mA)
    pi_coeff1_p: float = 1.0  # PI controller proportional coefficient for laser 1
    pi_coeff1_i: float = 0.5  # PI controller integral coefficient for laser 1
    pi_coeff2_p: float = 1.0  # PI controller proportional coefficient for laser 2
    pi_coeff2_i: float = 0.5  # PI controller integral coefficient for laser 2

    def to_dict(self) -> Dict[str, float]:
        """Convert to dictionary representation."""
        return {
            'temp1': self.temp1,
            'temp2': self.temp2,
            'current1': self.current1,
            'current2': self.current2,
            'pi_coeff1_p': self.pi_coeff1_p,
            'pi_coeff1_i': self.pi_coeff1_i,
            'pi_coeff2_p': self.pi_coeff2_p,
            'pi_coeff2_i': self.pi_coeff2_i
        }


@dataclass
class VariationParams:
    """Parameters for variation mode."""
    variation_type: VariationType
    # Static parameters (fixed during variation)
    static_temp1: float
    static_temp2: float
    static_current1: float
    static_current2: float
    # Variation range
    min_value: float  # Minimum value for varied parameter
    max_value: float  # Maximum value for varied parameter
    step: float  # Step size for variation
    # Time parameters
    time_step: int  # Time step in microseconds (20-100)
    delay_time: int  # Delay between measurements in milliseconds (3-10)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary representation."""
        return {
            'variation_type': self.variation_type.value,
            'static_temp1': self.static_temp1,
            'static_temp2': self.static_temp2,
            'static_current1': self.static_current1,
            'static_current2': self.static_current2,
            'min_value': self.min_value,
            'max_value': self.max_value,
            'step': self.step,
            'time_step': self.time_step,
            'delay_time': self.delay_time
        }


@dataclass
class Measurements:
    """Real-time measurements from the device."""
    # Photodiode currents
    current1: float  # Photodiode current for laser 1 (mA)
    current2: float  # Photodiode current for laser 2 (mA)
    # Temperatures
    temp1: float  # Temperature of laser 1 (°C)
    temp2: float  # Temperature of laser 2 (°C)
    temp_ext1: Optional[float] = None  # External thermistor 1 temperature (°C)
    temp_ext2: Optional[float] = None  # External thermistor 2 temperature (°C)
    # Power supply voltages
    voltage_3v3: float = 0.0  # 3.3V rail voltage
    voltage_5v1: float = 0.0  # 5V rail 1 voltage
    voltage_5v2: float = 0.0  # 5V rail 2 voltage
    voltage_7v0: float = 0.0  # 7V rail voltage
    # Metadata
    timestamp: Optional[datetime] = None
    message_id: Optional[int] = None
    to6_counter_lsb: Optional[int] = None
    to6_counter_msb: Optional[int] = None

    def __post_init__(self):
        """Set timestamp if not provided."""
        if self.timestamp is None:
            self.timestamp = datetime.now()

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary representation."""
        return {
            'current1': self.current1,
            'current2': self.current2,
            'temp1': self.temp1,
            'temp2': self.temp2,
            'temp_ext1': self.temp_ext1,
            'temp_ext2': self.temp_ext2,
            'voltage_3v3': self.voltage_3v3,
            'voltage_5v1': self.voltage_5v1,
            'voltage_5v2': self.voltage_5v2,
            'voltage_7v0': self.voltage_7v0,
            'timestamp': self.timestamp.isoformat() if self.timestamp else None,
            'message_id': self.message_id
        }

    def check_power_rails(self) -> Dict[str, bool]:
        """Check if power supply voltages are within acceptable range."""
        return {
            '3v3': 3.1 <= self.voltage_3v3 <= 3.5,
            '5v1': 4.8 <= self.voltage_5v1 <= 5.3,
            '5v2': 4.8 <= self.voltage_5v2 <= 5.3,
            '7v0': 6.5 <= self.voltage_7v0 <= 7.5
        }


@dataclass
class DeviceStatus:
    """Complete device status information."""
    state: DeviceState
    measurements: Optional[Measurements] = None
    is_connected: bool = False
    last_command_id: Optional[int] = None
    error_message: Optional[str] = None

    @property
    def is_idle(self) -> bool:
        """Check if device is idle."""
        return self.state == DeviceState.IDLE

    @property
    def is_running(self) -> bool:
        """Check if device is running a task."""
        return self.state == DeviceState.RUNNING

    @property
    def has_error(self) -> bool:
        """Check if device has any error."""
        return self.state >= DeviceState.ERROR

    @property
    def error_type(self) -> Optional[str]:
        """Get human-readable error type."""
        if not self.has_error:
            return None

        error_map = {
            DeviceState.ERROR_OVERHEAT: "Overheating",
            DeviceState.ERROR_POWER: "Power supply issue",
            DeviceState.ERROR_COMMUNICATION: "Communication error",
            DeviceState.ERROR_INVALID_COMMAND: "Invalid command"
        }
        return error_map.get(self.state, "Unknown error")

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary representation."""
        return {
            'state': self.state.value,
            'state_name': self.state.name,
            'measurements': self.measurements.to_dict() if self.measurements else None,
            'is_connected': self.is_connected,
            'last_command_id': self.last_command_id,
            'error_message': self.error_message,
            'is_idle': self.is_idle,
            'is_running': self.is_running,
            'has_error': self.has_error,
            'error_type': self.error_type
        }


@dataclass
class CalibrationData:
    """Calibration data for device sensors."""
    # Temperature calibration coefficients
    temp1_offset: float = 0.0
    temp1_scale: float = 1.0
    temp2_offset: float = 0.0
    temp2_scale: float = 1.0
    # Current calibration coefficients
    current1_offset: float = 0.0
    current1_scale: float = 1.0
    current2_offset: float = 0.0
    current2_scale: float = 1.0
    # Voltage calibration
    voltage_3v3_scale: float = 1.0
    voltage_5v1_scale: float = 1.0
    voltage_5v2_scale: float = 1.0
    voltage_7v0_scale: float = 1.0