import logging
import time
from typing import Optional, Dict, Any
from datetime import datetime

from ...api.models.laser import LaserParameters, LaserStatus
from .RadioPhotonic_PCB_PC_software import device_interaction as dev
from .RadioPhotonic_PCB_PC_software import device_commands as cmd

logger = logging.getLogger(__name__)


class LaserController:
    """
    Controller for laser control system.

    Communicates with RadioPhotonic board via serial port (115200 baud).
    Supports both manual control and automated scanning modes.
    """

    def __init__(self):
        self.prt = None  # Serial port object
        self.is_connected = False
        self.is_running = False
        self.current_parameters: Optional[LaserParameters] = None
        self.current_status = LaserStatus()
        self.last_data: Optional[Dict[str, Any]] = None

        # Default PI coefficients (multiplied by 256 as per device protocol)
        self.proportional_coeff_1 = int(10 * 256)
        self.proportional_coeff_2 = int(10 * 256)
        self.integral_coeff_1 = int(0.5 * 256)
        self.integral_coeff_2 = int(0.5 * 256)
        self.message_id = "00FF"

        logger.info("LaserController initialized")

    def start_cycle(self, parameters: LaserParameters) -> Dict[str, Any]:
        """
        Start laser control cycle with given parameters.

        Determines mode (manual vs scan) based on enable flags:
        - Manual mode: all enable_* flags are False
        - Scan mode: one enable_* flag is True

        Args:
            parameters: LaserParameters object with control settings

        Returns:
            Dictionary with success status and message
        """
        try:
            if not self.is_connected or self.prt is None:
                return {
                    "success": False,
                    "message": "Устройство не подключено. Сначала выполните подключение.",
                    "parameters": None
                }

            logger.info("=" * 60)
            logger.info("LASER CONTROL: START CYCLE")
            logger.info(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')}")
            logger.info("-" * 60)

            # Determine operation mode
            is_manual = not any([
                parameters.enable_t1,
                parameters.enable_t2,
                parameters.enable_c1,
                parameters.enable_c2
            ])

            if is_manual:
                logger.info("Mode: MANUAL CONTROL")
                result = self._start_manual_mode(parameters)
            else:
                logger.info("Mode: AUTOMATED SCAN")
                result = self._start_scan_mode(parameters)

            if result["success"]:
                self.current_parameters = parameters
                self.is_running = True
                self.current_status.is_running = True

            logger.info("=" * 60)
            return result

        except Exception as e:
            logger.error(f"Error starting laser cycle: {e}", exc_info=True)
            return {
                "success": False,
                "message": f"Ошибка при запуске цикла: {str(e)}",
                "parameters": None
            }

    def _start_manual_mode(self, parameters: LaserParameters) -> Dict[str, Any]:
        """
        Start manual control mode with fixed T1, T2, I1, I2 values.
        Uses DECODE_ENABLE (0x1111) command.
        """
        try:
            # Prepare control parameters
            params = {
                'Temp_1': parameters.min_temp_1,
                'Temp_2': parameters.min_temp_2,
                'Iset_1': parameters.min_current_1,
                'Iset_2': parameters.min_current_2,
                'ProportionalCoeff_1': self.proportional_coeff_1,
                'ProportionalCoeff_2': self.proportional_coeff_2,
                'IntegralCoeff_1': self.integral_coeff_1,
                'IntegralCoeff_2': self.integral_coeff_2,
                'Message_ID': self.message_id
            }

            logger.info(f"Sending manual control parameters:")
            logger.info(f"  T1: {params['Temp_1']}°C, T2: {params['Temp_2']}°C")
            logger.info(f"  I1: {params['Iset_1']} mA, I2: {params['Iset_2']} mA")

            # Send control parameters to device
            dev.send_control_parameters(self.prt, params)

            return {
                "success": True,
                "message": "Ручное управление запущено",
                "parameters": parameters.model_dump()
            }

        except Exception as e:
            logger.error(f"Error in manual mode: {e}", exc_info=True)
            raise

    def _start_scan_mode(self, parameters: LaserParameters) -> Dict[str, Any]:
        """
        Start automated scan mode.
        Uses TASK_ENABLE (0x7777) command with TaskType.
        """
        try:
            # Determine which parameter to scan
            if parameters.enable_c1:
                task_type = cmd.TaskType.ChangeCurrentLD1.value
                scan_param = "Current Laser 1"
                logger.info(f"Scanning Current Laser 1: {parameters.min_current_1} to {parameters.max_current_1} mA")
            elif parameters.enable_c2:
                task_type = cmd.TaskType.ChangeCurrentLD2.value
                scan_param = "Current Laser 2"
                logger.info(f"Scanning Current Laser 2: {parameters.min_current_2} to {parameters.max_current_2} mA")
            elif parameters.enable_t1:
                return {
                    "success": False,
                    "message": "Сканирование температуры Laser 1 не поддерживается устройством",
                    "parameters": None
                }
            elif parameters.enable_t2:
                return {
                    "success": False,
                    "message": "Сканирование температуры Laser 2 не поддерживается устройством",
                    "parameters": None
                }
            else:
                return {
                    "success": False,
                    "message": "Не выбран параметр для сканирования",
                    "parameters": None
                }

            # Build task parameters based on task type
            sending_param = {
                'TaskType': task_type,
                'Dt': parameters.delta_time / 1000.0,  # Convert μs to ms
                'Tau': parameters.tau,
                'ProportionalCoeff_1': self.proportional_coeff_1,
                'ProportionalCoeff_2': self.proportional_coeff_2,
                'IntegralCoeff_1': self.integral_coeff_1,
                'IntegralCoeff_2': self.integral_coeff_2
            }

            # Add scan-specific parameters
            if task_type == cmd.TaskType.ChangeCurrentLD1.value:
                sending_param.update({
                    'MinC1': parameters.min_current_1,
                    'MaxC1': parameters.max_current_1,
                    'DeltaC1': parameters.delta_current_1,
                    'T1': parameters.min_temp_1,  # Fixed
                    'I2': parameters.min_current_2,  # Fixed
                    'T2': parameters.min_temp_2  # Fixed
                })
            elif task_type == cmd.TaskType.ChangeCurrentLD2.value:
                sending_param.update({
                    'MinC2': parameters.min_current_2,
                    'MaxC2': parameters.max_current_2,
                    'DeltaC2': parameters.delta_current_2,
                    'T2': parameters.min_temp_2,  # Fixed
                    'I1': parameters.min_current_1,  # Fixed
                    'T1': parameters.min_temp_1  # Fixed
                })

            # Send task command to device
            dev.send_task_command(self.prt, sending_param)

            return {
                "success": True,
                "message": f"Сканирование запущено: {scan_param}",
                "parameters": parameters.model_dump()
            }

        except Exception as e:
            logger.error(f"Error in scan mode: {e}", exc_info=True)
            raise

    def stop_cycle(self) -> Dict[str, Any]:
        """
        Stop current laser control cycle.
        Sends reset command to device.

        Returns:
            Dictionary with success status and message
        """
        try:
            logger.info("=" * 60)
            logger.info("LASER CONTROL: STOP CYCLE")
            logger.info(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')}")
            logger.info("=" * 60)

            if self.is_connected and self.prt is not None:
                # Send reset command to device
                try:
                    dev.reset_port_settings(self.prt)
                    logger.info("Device reset command sent")
                except Exception as e:
                    logger.warning(f"Failed to send reset command: {e}")

            self.is_running = False
            self.current_status.is_running = False
            self.current_parameters = None

            return {
                "success": True,
                "message": "Цикл управления лазером остановлен"
            }

        except Exception as e:
            logger.error(f"Error stopping laser cycle: {e}", exc_info=True)
            return {
                "success": False,
                "message": f"Ошибка при остановке цикла: {str(e)}"
            }

    def start_manual_direct(self, t1: float, t2: float, i1: float, i2: float) -> Dict[str, Any]:
        """
        Start manual control mode with direct T1, T2, I1, I2 parameters.
        Simplified interface for manual control.

        Args:
            t1: Temperature for Laser 1 in Celsius
            t2: Temperature for Laser 2 in Celsius
            i1: Current for Laser 1 in mA
            i2: Current for Laser 2 in mA

        Returns:
            Dictionary with success status and message
        """
        try:
            # Check connection status
            if not self.is_connected:
                logger.error("Device not connected")
                return {
                    "success": False,
                    "message": "Устройство не подключено. Нажмите кнопку 'Подключить' в настройках.",
                }

            if self.prt is None:
                logger.error("Serial port is None")
                return {
                    "success": False,
                    "message": "Порт не инициализирован. Переподключите устройство.",
                }

            # Check if port is actually open
            if not self.prt.is_open:
                logger.error("Serial port is not open")
                return {
                    "success": False,
                    "message": "Порт не открыт. Переподключите устройство.",
                }

            logger.info("=" * 60)
            logger.info("LASER CONTROL: START MANUAL MODE (Direct)")
            logger.info(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')}")
            logger.info(f"Port: {self.prt.port}, Open: {self.prt.is_open}")
            logger.info(f"  T1: {t1}°C, T2: {t2}°C")
            logger.info(f"  I1: {i1} mA, I2: {i2} mA")
            logger.info("=" * 60)

            # Prepare control parameters
            params = {
                'Temp_1': t1,
                'Temp_2': t2,
                'Iset_1': i1,
                'Iset_2': i2,
                'ProportionalCoeff_1': self.proportional_coeff_1,
                'ProportionalCoeff_2': self.proportional_coeff_2,
                'IntegralCoeff_1': self.integral_coeff_1,
                'IntegralCoeff_2': self.integral_coeff_2,
                'Message_ID': self.message_id
            }

            # Send control parameters to device
            logger.info("Sending control parameters to device...")
            dev.send_control_parameters(self.prt, params)
            logger.info("Control parameters sent successfully")

            self.is_running = True
            self.current_status.is_running = True

            return {
                "success": True,
                "message": "Ручное управление запущено",
            }

        except Exception as e:
            logger.error(f"Error starting manual control: {e}", exc_info=True)
            # Reset connection status if error
            self.is_connected = False
            return {
                "success": False,
                "message": f"Ошибка при запуске: {str(e)}. Попробуйте переподключить устройство.",
            }

    def get_status(self) -> LaserStatus:
        """
        Get current laser status by requesting data from device.
        Uses TRANS_ENABLE (0x4444) command.

        Returns:
            LaserStatus object with current state
        """
        self.current_status.connected = self.is_connected
        self.current_status.is_running = self.is_running

        if self.is_connected and self.prt is not None:
            try:
                # Request data from device
                data = dev.request_data(self.prt)

                if data and isinstance(data, dict):
                    # Update status from device data
                    self.last_data = data
                    self.current_status.temp_1 = data.get('Temp_1', 0.0)
                    self.current_status.temp_2 = data.get('Temp_2', 0.0)
                    self.current_status.current_1 = data.get('I1', 0.0)
                    self.current_status.current_2 = data.get('I2', 0.0)
                    self.current_status.temp_ext_1 = data.get('Temp_Ext_1', 0.0)
                    self.current_status.temp_ext_2 = data.get('Temp_Ext_2', 0.0)
                    self.current_status.voltage_3v3 = data.get('MON_3V3', 0.0)
                    self.current_status.voltage_5v1 = data.get('MON_5V1', 0.0)
                    self.current_status.voltage_5v2 = data.get('MON_5V2', 0.0)
                    self.current_status.voltage_7v0 = data.get('MON_7V0', 0.0)

            except Exception as e:
                logger.warning(f"Error requesting status from device: {e}")
                # Keep previous status values on error

        return self.current_status

    def connect(self, port: Optional[str] = None) -> Dict[str, Any]:
        """
        Connect to laser control hardware via serial port.
        Auto-detects USB serial ports if port not specified.

        Args:
            port: Serial port to connect to (e.g., '/dev/ttyUSB0')
                  If None, will auto-detect USB ports

        Returns:
            Dictionary with success status and message
        """
        try:
            logger.info(f"Attempting to connect to laser hardware on port: {port or 'auto-detect'}")

            if self.is_connected:
                logger.warning("Already connected to device")
                return {
                    "success": True,
                    "message": "Уже подключено к устройству",
                    "port": str(self.prt.port) if self.prt else "unknown"
                }

            # Create port connection (auto-detect if port not specified)
            if port:
                # Manual port specification
                try:
                    self.prt = cmd.setup_port_connection(port=port, baudrate=115200, timeout_sec=1)
                    cmd.open_port(self.prt)
                    dev.reset_port_settings(self.prt)
                except Exception as e:
                    logger.error(f"Failed to connect to specified port {port}: {e}")
                    return {
                        "success": False,
                        "message": f"Не удалось подключиться к порту {port}: {str(e)}",
                        "port": port
                    }
            else:
                # Auto-detect USB ports
                self.prt = dev.create_port_connection()

            if self.prt is None:
                logger.error("Failed to create port connection")
                return {
                    "success": False,
                    "message": "Не удалось найти устройство. Проверьте подключение USB.",
                    "port": None
                }

            self.is_connected = True
            self.current_status.connected = True

            port_name = self.prt.port if hasattr(self.prt, 'port') else "unknown"
            logger.info(f"Successfully connected to laser hardware on {port_name}")

            # Request initial status
            try:
                time.sleep(0.2)  # Give device time to initialize
                self.get_status()
            except Exception as e:
                logger.warning(f"Failed to get initial status: {e}")

            return {
                "success": True,
                "message": f"Подключено к устройству на порту {port_name}",
                "port": port_name
            }

        except Exception as e:
            logger.error(f"Error connecting to laser hardware: {e}", exc_info=True)
            self.is_connected = False
            self.prt = None
            return {
                "success": False,
                "message": f"Ошибка подключения: {str(e)}",
                "port": port
            }

    def disconnect(self) -> Dict[str, Any]:
        """
        Disconnect from laser control hardware.
        Stops any running cycle and closes serial port.

        Returns:
            Dictionary with success status and message
        """
        try:
            logger.info("Disconnecting from laser hardware")

            # Stop any running cycle first
            if self.is_running:
                self.stop_cycle()

            # Close serial port
            if self.prt is not None:
                try:
                    cmd.close_port(self.prt)
                    logger.info("Serial port closed")
                except Exception as e:
                    logger.warning(f"Error closing serial port: {e}")

                self.prt = None

            self.is_connected = False
            self.current_status.connected = False
            self.last_data = None

            logger.info("Successfully disconnected from laser hardware")

            return {
                "success": True,
                "message": "Отключено от устройства"
            }

        except Exception as e:
            logger.error(f"Error disconnecting from laser hardware: {e}", exc_info=True)
            return {
                "success": False,
                "message": f"Ошибка отключения: {str(e)}"
            }
