"""
工作流执行器模块
Workflow Executors Module

基于单一硬件接口的工作流执行器实现
支持Modbus、HTTP、PyLabRobot和代理模式
"""
import time
import json
import asyncio
from typing import Dict, Any, List, Optional, TYPE_CHECKING
from abc import ABC, abstractmethod

if TYPE_CHECKING:
    from unilabos.devices.work_station.workstation_base import WorkstationBase

from unilabos.utils.log import logger


class WorkflowExecutor(ABC):
    """工作流执行器基类 - 基于单一硬件接口"""
    
    def __init__(self, workstation: 'WorkstationBase'):
        self.workstation = workstation
        self.hardware_interface = workstation.hardware_interface
        self.material_management = workstation.material_management
    
    @abstractmethod
    def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
        """执行工作流"""
        pass
    
    @abstractmethod
    def stop_workflow(self, emergency: bool = False) -> bool:
        """停止工作流"""
        pass
    
    def call_device(self, method: str, *args, **kwargs) -> Any:
        """调用设备方法的统一接口"""
        return self.workstation.call_device_method(method, *args, **kwargs)
    
    def get_device_status(self) -> Dict[str, Any]:
        """获取设备状态"""
        return self.workstation.get_device_status()


class ModbusWorkflowExecutor(WorkflowExecutor):
    """Modbus工作流执行器 - 适配 coin_cell_assembly_system"""
    
    def __init__(self, workstation: 'WorkstationBase'):
        super().__init__(workstation)
        
        # 验证Modbus接口
        if not (hasattr(self.hardware_interface, 'write_register') and 
                hasattr(self.hardware_interface, 'read_register')):
            raise RuntimeError("工作站硬件接口不是有效的Modbus客户端")
    
    def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
        """执行Modbus工作流"""
        if workflow_name == "battery_manufacturing":
            return self._execute_battery_manufacturing(parameters)
        elif workflow_name == "material_loading":
            return self._execute_material_loading(parameters)
        elif workflow_name == "quality_check":
            return self._execute_quality_check(parameters)
        else:
            logger.warning(f"不支持的Modbus工作流: {workflow_name}")
            return False
    
    def _execute_battery_manufacturing(self, parameters: Dict[str, Any]) -> bool:
        """执行电池制造工作流"""
        try:
            # 1. 物料准备检查
            available_slot = self._find_available_press_slot()
            if not available_slot:
                raise RuntimeError("没有可用的压制槽")
            
            logger.info(f"找到可用压制槽: {available_slot}")
            
            # 2. 设置工艺参数（直接调用Modbus接口）
            if "electrolyte_num" in parameters:
                self.hardware_interface.write_register('REG_MSG_ELECTROLYTE_NUM', parameters["electrolyte_num"])
                logger.info(f"设置电解液编号: {parameters['electrolyte_num']}")
            
            if "electrolyte_volume" in parameters:
                self.hardware_interface.write_register('REG_MSG_ELECTROLYTE_VOLUME', 
                                                      parameters["electrolyte_volume"], 
                                                      data_type="FLOAT32")
                logger.info(f"设置电解液体积: {parameters['electrolyte_volume']}")
            
            if "assembly_pressure" in parameters:
                self.hardware_interface.write_register('REG_MSG_ASSEMBLY_PRESSURE',
                                                      parameters["assembly_pressure"],
                                                      data_type="FLOAT32")
                logger.info(f"设置装配压力: {parameters['assembly_pressure']}")
            
            # 3. 启动制造流程
            self.hardware_interface.write_register('COIL_SYS_START_CMD', True)
            logger.info("启动电池制造流程")
            
            # 4. 确认启动成功
            time.sleep(0.5)
            status = self.hardware_interface.read_register('COIL_SYS_START_STATUS', count=1)
            success = status[0] if status else False
            
            if success:
                logger.info(f"电池制造工作流启动成功，参数: {parameters}")
            else:
                logger.error("电池制造工作流启动失败")
            
            return success
            
        except Exception as e:
            logger.error(f"执行电池制造工作流失败: {e}")
            return False
    
    def _execute_material_loading(self, parameters: Dict[str, Any]) -> bool:
        """执行物料装载工作流"""
        try:
            material_type = parameters.get('material_type', 'cathode')
            position = parameters.get('position', 'A1')
            
            logger.info(f"开始物料装载: {material_type} -> {position}")
            
            # 设置物料类型和位置
            self.hardware_interface.write_register('REG_MATERIAL_TYPE', material_type)
            self.hardware_interface.write_register('REG_MATERIAL_POSITION', position)
            
            # 启动装载
            self.hardware_interface.write_register('COIL_LOAD_START', True)
            
            # 等待装载完成
            timeout = parameters.get('timeout', 30)
            start_time = time.time()
            
            while time.time() - start_time < timeout:
                status = self.hardware_interface.read_register('COIL_LOAD_COMPLETE', count=1)
                if status and status[0]:
                    logger.info(f"物料装载完成: {material_type} -> {position}")
                    return True
                time.sleep(0.5)
            
            logger.error(f"物料装载超时: {material_type} -> {position}")
            return False
            
        except Exception as e:
            logger.error(f"执行物料装载失败: {e}")
            return False
    
    def _execute_quality_check(self, parameters: Dict[str, Any]) -> bool:
        """执行质量检测工作流"""
        try:
            check_type = parameters.get('check_type', 'dimensional')
            
            logger.info(f"开始质量检测: {check_type}")
            
            # 启动质量检测
            self.hardware_interface.write_register('REG_QC_TYPE', check_type)
            self.hardware_interface.write_register('COIL_QC_START', True)
            
            # 等待检测完成
            timeout = parameters.get('timeout', 60)
            start_time = time.time()
            
            while time.time() - start_time < timeout:
                status = self.hardware_interface.read_register('COIL_QC_COMPLETE', count=1)
                if status and status[0]:
                    # 读取检测结果
                    result = self.hardware_interface.read_register('REG_QC_RESULT', count=1)
                    passed = result[0] if result else False
                    
                    if passed:
                        logger.info(f"质量检测通过: {check_type}")
                        return True
                    else:
                        logger.warning(f"质量检测失败: {check_type}")
                        return False
                        
                time.sleep(1.0)
            
            logger.error(f"质量检测超时: {check_type}")
            return False
            
        except Exception as e:
            logger.error(f"执行质量检测失败: {e}")
            return False
    
    def _find_available_press_slot(self) -> Optional[str]:
        """查找可用压制槽"""
        try:
            press_slots = self.material_management.find_by_category("battery_press_slot")
            for slot in press_slots:
                if hasattr(slot, 'has_battery') and not slot.has_battery():
                    return slot.name
            return None
        except:
            # 如果物料管理系统不可用，返回默认槽位
            return "A1"
    
    def stop_workflow(self, emergency: bool = False) -> bool:
        """停止工作流"""
        try:
            if emergency:
                self.hardware_interface.write_register('COIL_SYS_RESET_CMD', True)
                logger.warning("执行紧急停止")
            else:
                self.hardware_interface.write_register('COIL_SYS_STOP_CMD', True)
                logger.info("执行正常停止")
            
            time.sleep(0.5)
            status = self.hardware_interface.read_register('COIL_SYS_STOP_STATUS', count=1)
            return status[0] if status else False
            
        except Exception as e:
            logger.error(f"停止Modbus工作流失败: {e}")
            return False


class HttpWorkflowExecutor(WorkflowExecutor):
    """HTTP工作流执行器 - 适配 reaction_station_bioyong"""
    
    def __init__(self, workstation: 'WorkstationBase'):
        super().__init__(workstation)
        
        # 验证HTTP接口
        if not (hasattr(self.hardware_interface, 'post') or 
                hasattr(self.hardware_interface, 'get')):
            raise RuntimeError("工作站硬件接口不是有效的HTTP客户端")
    
    def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
        """执行HTTP工作流"""
        try:
            if workflow_name == "reaction_synthesis":
                return self._execute_reaction_synthesis(parameters)
            elif workflow_name == "liquid_feeding":
                return self._execute_liquid_feeding(parameters)
            elif workflow_name == "temperature_control":
                return self._execute_temperature_control(parameters)
            else:
                logger.warning(f"不支持的HTTP工作流: {workflow_name}")
                return False
                
        except Exception as e:
            logger.error(f"执行HTTP工作流失败: {e}")
            return False
    
    def _execute_reaction_synthesis(self, parameters: Dict[str, Any]) -> bool:
        """执行反应合成工作流"""
        try:
            # 1. 设置工作流序列
            sequence = self._build_reaction_sequence(parameters)
            self._call_rpc_method('set_workflow_sequence', json.dumps(sequence))
            
            # 2. 设置反应参数
            if parameters.get('temperature'):
                self._call_rpc_method('set_temperature', parameters['temperature'])
            
            if parameters.get('pressure'):
                self._call_rpc_method('set_pressure', parameters['pressure'])
            
            if parameters.get('stirring_speed'):
                self._call_rpc_method('set_stirring_speed', parameters['stirring_speed'])
            
            # 3. 执行工作流
            result = self._call_rpc_method('execute_current_sequence', {
                "task_name": "reaction_synthesis"
            })
            
            success = result.get('success', False)
            if success:
                logger.info("反应合成工作流执行成功")
            else:
                logger.error(f"反应合成工作流执行失败: {result.get('error', '未知错误')}")
            
            return success
            
        except Exception as e:
            logger.error(f"执行反应合成工作流失败: {e}")
            return False
    
    def _execute_liquid_feeding(self, parameters: Dict[str, Any]) -> bool:
        """执行液体投料工作流"""
        try:
            reagents = parameters.get('reagents', [])
            volumes = parameters.get('volumes', [])
            
            if len(reagents) != len(volumes):
                raise ValueError("试剂列表和体积列表长度不匹配")
            
            # 执行投料序列
            for reagent, volume in zip(reagents, volumes):
                result = self._call_rpc_method('feed_liquid', {
                    'reagent': reagent,
                    'volume': volume
                })
                
                if not result.get('success', False):
                    logger.error(f"投料失败: {reagent} {volume}mL")
                    return False
                
                logger.info(f"投料成功: {reagent} {volume}mL")
            
            return True
            
        except Exception as e:
            logger.error(f"执行液体投料失败: {e}")
            return False
    
    def _execute_temperature_control(self, parameters: Dict[str, Any]) -> bool:
        """执行温度控制工作流"""
        try:
            target_temp = parameters.get('temperature', 25)
            hold_time = parameters.get('hold_time', 300)  # 秒
            
            # 设置目标温度
            result = self._call_rpc_method('set_temperature', target_temp)
            if not result.get('success', False):
                logger.error(f"设置温度失败: {target_temp}°C")
                return False
            
            # 等待温度稳定
            logger.info(f"等待温度稳定到 {target_temp}°C")
            
            # 保持温度指定时间
            if hold_time > 0:
                logger.info(f"保持温度 {hold_time} 秒")
                time.sleep(hold_time)
            
            return True
            
        except Exception as e:
            logger.error(f"执行温度控制失败: {e}")
            return False
    
    def _build_reaction_sequence(self, parameters: Dict[str, Any]) -> List[str]:
        """构建反应合成工作流序列"""
        sequence = []
        
        # 添加预处理步骤
        if parameters.get('purge_with_inert'):
            sequence.append("purge_inert_gas")
        
        # 添加温度设置
        if parameters.get('temperature'):
            sequence.append(f"set_temperature_{parameters['temperature']}")
        
        # 添加压力设置
        if parameters.get('pressure'):
            sequence.append(f"set_pressure_{parameters['pressure']}")
        
        # 添加搅拌设置
        if parameters.get('stirring_speed'):
            sequence.append(f"set_stirring_{parameters['stirring_speed']}")
        
        # 添加反应步骤
        sequence.extend([
            "start_reaction",
            "monitor_progress", 
            "complete_reaction"
        ])
        
        # 添加后处理步骤
        if parameters.get('cooling_required'):
            sequence.append("cool_down")
        
        return sequence
    
    def _call_rpc_method(self, method: str, params: Any = None) -> Dict[str, Any]:
        """调用RPC方法"""
        try:
            if hasattr(self.hardware_interface, method):
                # 直接方法调用
                if isinstance(params, dict):
                    params = json.dumps(params)
                elif params is None:
                    params = ""
                return getattr(self.hardware_interface, method)(params)
            else:
                # HTTP请求调用
                if hasattr(self.hardware_interface, 'post'):
                    response = self.hardware_interface.post(f"/api/{method}", json=params)
                    return response.json()
                else:
                    raise AttributeError(f"HTTP接口不支持方法: {method}")
        except Exception as e:
            logger.error(f"调用RPC方法失败 {method}: {e}")
            return {'success': False, 'error': str(e)}
    
    def stop_workflow(self, emergency: bool = False) -> bool:
        """停止工作流"""
        try:
            if emergency:
                result = self._call_rpc_method('scheduler_reset')
            else:
                result = self._call_rpc_method('scheduler_stop')
            
            return result.get('success', False)
            
        except Exception as e:
            logger.error(f"停止HTTP工作流失败: {e}")
            return False


class PyLabRobotWorkflowExecutor(WorkflowExecutor):
    """PyLabRobot工作流执行器 - 适配 prcxi.py"""
    
    def __init__(self, workstation: 'WorkstationBase'):
        super().__init__(workstation)
        
        # 验证PyLabRobot接口
        if not (hasattr(self.hardware_interface, 'transfer_liquid') or 
                hasattr(self.hardware_interface, 'pickup_tips')):
            raise RuntimeError("工作站硬件接口不是有效的PyLabRobot设备")
    
    def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
        """执行PyLabRobot工作流"""
        try:
            if workflow_name == "liquid_transfer":
                return self._execute_liquid_transfer(parameters)
            elif workflow_name == "tip_pickup_drop":
                return self._execute_tip_operations(parameters)
            elif workflow_name == "plate_handling":
                return self._execute_plate_handling(parameters)
            else:
                logger.warning(f"不支持的PyLabRobot工作流: {workflow_name}")
                return False
                
        except Exception as e:
            logger.error(f"执行PyLabRobot工作流失败: {e}")
            return False
    
    def _execute_liquid_transfer(self, parameters: Dict[str, Any]) -> bool:
        """执行液体转移工作流"""
        try:
            # 1. 解析物料引用
            sources = self._resolve_containers(parameters.get('sources', []))
            targets = self._resolve_containers(parameters.get('targets', []))
            tip_racks = self._resolve_tip_racks(parameters.get('tip_racks', []))
            
            if not sources or not targets:
                raise ValueError("液体转移需要指定源容器和目标容器")
            
            if not tip_racks:
                logger.warning("未指定枪头架，将尝试自动查找")
                tip_racks = self._find_available_tip_racks()
            
            # 2. 执行液体转移
            volumes = parameters.get('volumes', [])
            if not volumes:
                volumes = [100.0] * len(sources)  # 默认体积
            
            # 如果是同步接口
            if hasattr(self.hardware_interface, 'transfer_liquid'):
                result = self.hardware_interface.transfer_liquid(
                    sources=sources,
                    targets=targets,
                    tip_racks=tip_racks,
                    asp_vols=volumes,
                    dis_vols=volumes,
                    **parameters.get('options', {})
                )
            else:
                # 异步接口需要特殊处理
                asyncio.run(self._async_liquid_transfer(sources, targets, tip_racks, volumes, parameters))
                result = True
            
            if result:
                logger.info(f"液体转移工作流完成: {len(sources)}个源 -> {len(targets)}个目标")
            
            return bool(result)
            
        except Exception as e:
            logger.error(f"执行液体转移失败: {e}")
            return False
    
    async def _async_liquid_transfer(self, sources, targets, tip_racks, volumes, parameters):
        """异步液体转移"""
        await self.hardware_interface.transfer_liquid(
            sources=sources,
            targets=targets,
            tip_racks=tip_racks,
            asp_vols=volumes,
            dis_vols=volumes,
            **parameters.get('options', {})
        )
    
    def _execute_tip_operations(self, parameters: Dict[str, Any]) -> bool:
        """执行枪头操作工作流"""
        try:
            operation = parameters.get('operation', 'pickup')
            tip_racks = self._resolve_tip_racks(parameters.get('tip_racks', []))
            
            if not tip_racks:
                raise ValueError("枪头操作需要指定枪头架")
            
            if operation == 'pickup':
                result = self.hardware_interface.pickup_tips(tip_racks[0])
                logger.info("枪头拾取完成")
            elif operation == 'drop':
                result = self.hardware_interface.drop_tips()
                logger.info("枪头丢弃完成")
            else:
                raise ValueError(f"不支持的枪头操作: {operation}")
            
            return bool(result)
            
        except Exception as e:
            logger.error(f"执行枪头操作失败: {e}")
            return False
    
    def _execute_plate_handling(self, parameters: Dict[str, Any]) -> bool:
        """执行板类处理工作流"""
        try:
            operation = parameters.get('operation', 'move')
            source_position = parameters.get('source_position')
            target_position = parameters.get('target_position')
            
            if operation == 'move' and source_position and target_position:
                # 移动板类
                result = self.hardware_interface.move_plate(source_position, target_position)
                logger.info(f"板类移动完成: {source_position} -> {target_position}")
            else:
                logger.warning(f"不支持的板类操作或参数不完整: {operation}")
                return False
            
            return bool(result)
            
        except Exception as e:
            logger.error(f"执行板类处理失败: {e}")
            return False
    
    def _resolve_containers(self, container_names: List[str]):
        """解析容器名称为实际容器对象"""
        containers = []
        for name in container_names:
            try:
                container = self.material_management.find_material_by_id(name)
                if container:
                    containers.append(container)
                else:
                    logger.warning(f"未找到容器: {name}")
            except:
                logger.warning(f"解析容器失败: {name}")
        return containers
    
    def _resolve_tip_racks(self, tip_rack_names: List[str]):
        """解析枪头架名称为实际对象"""
        tip_racks = []
        for name in tip_rack_names:
            try:
                tip_rack = self.material_management.find_by_category("tip_rack")
                matching_racks = [rack for rack in tip_rack if rack.name == name]
                if matching_racks:
                    tip_racks.extend(matching_racks)
                else:
                    logger.warning(f"未找到枪头架: {name}")
            except:
                logger.warning(f"解析枪头架失败: {name}")
        return tip_racks
    
    def _find_available_tip_racks(self):
        """查找可用的枪头架"""
        try:
            tip_racks = self.material_management.find_by_category("tip_rack")
            available_racks = [rack for rack in tip_racks if hasattr(rack, 'has_tips') and rack.has_tips()]
            return available_racks[:1]  # 返回第一个可用的枪头架
        except:
            return []
    
    def stop_workflow(self, emergency: bool = False) -> bool:
        """停止工作流"""
        try:
            if emergency:
                if hasattr(self.hardware_interface, 'emergency_stop'):
                    return self.hardware_interface.emergency_stop()
                else:
                    logger.warning("设备不支持紧急停止")
                    return False
            else:
                if hasattr(self.hardware_interface, 'graceful_stop'):
                    return self.hardware_interface.graceful_stop()
                elif hasattr(self.hardware_interface, 'stop'):
                    return self.hardware_interface.stop()
                else:
                    logger.warning("设备不支持优雅停止")
                    return False
                
        except Exception as e:
            logger.error(f"停止PyLabRobot工作流失败: {e}")
            return False


class ProxyWorkflowExecutor(WorkflowExecutor):
    """代理工作流执行器 - 处理代理模式的工作流"""
    
    def __init__(self, workstation: 'WorkstationBase'):
        super().__init__(workstation)
        
        # 验证代理接口
        if not isinstance(self.hardware_interface, str) or not self.hardware_interface.startswith("proxy:"):
            raise RuntimeError("工作站硬件接口不是有效的代理字符串")
        
        self.device_id = self.hardware_interface[6:]  # 移除 "proxy:" 前缀
    
    def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
        """执行代理工作流"""
        try:
            # 通过协议节点调用目标设备的工作流
            if self.workstation._workstation_node:
                return self.workstation._workstation_node.call_device_method(
                    self.device_id, 'execute_workflow', workflow_name, parameters
                )
            else:
                logger.error("代理模式需要workstation_node")
                return False
                
        except Exception as e:
            logger.error(f"执行代理工作流失败: {e}")
            return False
    
    def stop_workflow(self, emergency: bool = False) -> bool:
        """停止代理工作流"""
        try:
            if self.workstation._workstation_node:
                return self.workstation._workstation_node.call_device_method(
                    self.device_id, 'stop_workflow', emergency
                )
            else:
                logger.error("代理模式需要workstation_node")
                return False
                
        except Exception as e:
            logger.error(f"停止代理工作流失败: {e}")
            return False


# 辅助函数
def get_executor_for_interface(hardware_interface) -> str:
    """根据硬件接口类型获取执行器类型名称"""
    if isinstance(hardware_interface, str) and hardware_interface.startswith("proxy:"):
        return "ProxyWorkflowExecutor"
    elif hasattr(hardware_interface, 'write_register') and hasattr(hardware_interface, 'read_register'):
        return "ModbusWorkflowExecutor"
    elif hasattr(hardware_interface, 'post') or hasattr(hardware_interface, 'get'):
        return "HttpWorkflowExecutor"
    elif hasattr(hardware_interface, 'transfer_liquid') or hasattr(hardware_interface, 'pickup_tips'):
        return "PyLabRobotWorkflowExecutor"
    else:
        return "UnknownExecutor"
