//! AoE (ADS over EtherCAT) 完整实现
//!
//! 对齐 C# AoEInstance + AoESubscriptionManager，合并原 aoe_advanced.rs。
//! 提供:
//! - AoEInstance: ADS 读写、设备信息、状态控制、通知管理、配置
//! - AoeSubscriptionManager: 多订阅管理、自动清理
//! - AoeTransMode/AoeSubscription: 订阅配置
//!
//! # 异步使用 (对齐 C# AoEInstance.ReadAsync / WriteAsync)
//!
//! 本 crate 提供**双轨**异步 API:
//!
//! ## 轨道 1: std::thread (无依赖)
//!
//! ```ignore
//! let handle = AoEInstance::read_blocking(0, 1, 0x4020, 0x0000, 4);
//! let data: Option<Vec<u8>> = handle.join().unwrap()?;
//!
//! let handle = AoEInstance::write_blocking(0, 1, 0x4020, 0x0000, vec![0x01, 0x02]);
//! handle.join().unwrap()?;
//! ```
//!
//! ## 轨道 2: tokio async (可选 feature `async-tokio`)
//!
//! ```ignore
//! let aoe = slave.aoe();
//! let data = aoe.read_async(0x4020, 0x0000, 4).await?;
//! aoe.write_async(0x4020, 0x0000, vec![0x01, 0x02]).await?;
//! ```
//!
//! ## 通知回调 -> async 通道
//!
//! 从 DLL 回调推送到 async 代码, 使用 `tokio::sync::mpsc::unbounded_channel`:
//!
//! ```ignore
//! let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
//! // 在 subscribe 时注册回调, 回调体中 `tx.send(data)` 即可
//! while let Some(data) = rx.recv().await {
//!     // 在 async 上下文中处理数据
//! }
//! ```

use std::collections::HashMap;
use crate::data::error::{DarraError, Result};
use crate::utils::ffi::{self, AOENotificationCallback};
use std::os::raw::c_void;

/// 默认超时 (微秒)
const DEFAULT_TIMEOUT_US: i32 = 500_000;

// ===================== AoE 结果错误码 (ETG.1020 Table 16, 对齐 C# AoEResultCode) =====================

/// AoE 结果错误码 (ETG.1020 Table 16, 对齐 C# AoEResultCode).
///
/// 数值与 C#/C++/Java/Python 一致, 高字节 = 错误类 (0x0700=ADS device, 0x0740=client),
/// 低字节 = 具体错误码.
///
/// 与 [`DarraError::AoeFailed`] 互补: `DarraError::AoeFailed` 表示"AoE 操作失败"的通用错,
/// 而 `AoEResultCode` 是 ADS 协议层的具体子码, 由上层从 DLL 错误结构中拆出后比对枚举.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum AoEResultCode {
    NoError                       = 0x0000,
    InternalError                 = 0x0001,
    NoRealTime                    = 0x0002,
    AllocationLockedMemoryError   = 0x0003,
    InsertMailboxError            = 0x0004,
    WrongReceiveHMsg              = 0x0005,
    TargetPortNotFound            = 0x0006,
    TargetMachineNotFound         = 0x0007,
    UnknownCommandId              = 0x0008,
    BadTaskId                     = 0x0009,
    NoIO                          = 0x000A,
    UnknownAmsCommand             = 0x000B,
    Win32Error                    = 0x000C,
    PortNotConnected              = 0x000D,
    InvalidAmsLength              = 0x000E,
    InvalidAmsNetId               = 0x000F,
    LowInstLevel                  = 0x0010,
    NoDebugAvailable              = 0x0011,
    PortDisabled                  = 0x0012,
    PortAlreadyConnected          = 0x0013,
    AmsSyncWin32Error             = 0x0014,
    AmsSyncTimeout                = 0x0015,
    AmsSyncAmsError               = 0x0016,
    AmsSyncNoIndexMap             = 0x0017,
    InvalidAmsPort                = 0x0018,
    NoMemory                      = 0x0019,
    TcpSendError                  = 0x001A,
    HostUnreachable               = 0x001B,
    InvalidAmsFragment            = 0x001C,
    WsaServiceNotStarted          = 0x274D,
    DeviceInvalidGroup            = 0x0700,
    DeviceInvalidOffset           = 0x0701,
    DeviceInvalidAccess           = 0x0702,
    DeviceInvalidSize             = 0x0703,
    DeviceInvalidData             = 0x0704,
    DeviceNotReady                = 0x0705,
    DeviceBusy                    = 0x0706,
    DeviceInvalidContext          = 0x0707,
    DeviceNotFound                = 0x0708,
    DeviceAlreadyExists           = 0x0709,
    DeviceSymbolNotFound          = 0x070A,
    DeviceSymbolVersionInvalid    = 0x070B,
    DeviceInvalidState            = 0x070C,
    DeviceTimeout                 = 0x0712,
    DeviceNoInterfaceQuery        = 0x0713,
    DeviceInvalidInterface        = 0x0714,
    DeviceInvalidClassId          = 0x0715,
    DeviceInvalidObjectId         = 0x0716,
    DevicePending                 = 0x0717,
    DeviceAborted                 = 0x0718,
    DeviceWarning                 = 0x0719,
    DeviceInvalidArrayIndex       = 0x071A,
    DeviceSymbolNotActive         = 0x071B,
    DeviceAccessDenied            = 0x071C,
    ClientError                   = 0x0740,
    ClientInvalidParam            = 0x0741,
    ClientListEmpty               = 0x0742,
    ClientVarAlreadyInUse         = 0x0743,
    ClientInvokeTimeout           = 0x0744,
    ClientPortNotOpen             = 0x0745,
    ClientNotStarted              = 0x0746,
    ClientQueueFull               = 0x0750,
}

impl AoEResultCode {
    /// 从原始 u32 数值匹配枚举 (未匹配返回 None).
    pub fn from_u32(code: u32) -> Option<Self> {
        Some(match code {
            0x0000 => Self::NoError,
            0x0001 => Self::InternalError,
            0x0002 => Self::NoRealTime,
            0x0003 => Self::AllocationLockedMemoryError,
            0x0004 => Self::InsertMailboxError,
            0x0005 => Self::WrongReceiveHMsg,
            0x0006 => Self::TargetPortNotFound,
            0x0007 => Self::TargetMachineNotFound,
            0x0008 => Self::UnknownCommandId,
            0x0009 => Self::BadTaskId,
            0x000A => Self::NoIO,
            0x000B => Self::UnknownAmsCommand,
            0x000C => Self::Win32Error,
            0x000D => Self::PortNotConnected,
            0x000E => Self::InvalidAmsLength,
            0x000F => Self::InvalidAmsNetId,
            0x0010 => Self::LowInstLevel,
            0x0011 => Self::NoDebugAvailable,
            0x0012 => Self::PortDisabled,
            0x0013 => Self::PortAlreadyConnected,
            0x0014 => Self::AmsSyncWin32Error,
            0x0015 => Self::AmsSyncTimeout,
            0x0016 => Self::AmsSyncAmsError,
            0x0017 => Self::AmsSyncNoIndexMap,
            0x0018 => Self::InvalidAmsPort,
            0x0019 => Self::NoMemory,
            0x001A => Self::TcpSendError,
            0x001B => Self::HostUnreachable,
            0x001C => Self::InvalidAmsFragment,
            0x274D => Self::WsaServiceNotStarted,
            0x0700 => Self::DeviceInvalidGroup,
            0x0701 => Self::DeviceInvalidOffset,
            0x0702 => Self::DeviceInvalidAccess,
            0x0703 => Self::DeviceInvalidSize,
            0x0704 => Self::DeviceInvalidData,
            0x0705 => Self::DeviceNotReady,
            0x0706 => Self::DeviceBusy,
            0x0707 => Self::DeviceInvalidContext,
            0x0708 => Self::DeviceNotFound,
            0x0709 => Self::DeviceAlreadyExists,
            0x070A => Self::DeviceSymbolNotFound,
            0x070B => Self::DeviceSymbolVersionInvalid,
            0x070C => Self::DeviceInvalidState,
            0x0712 => Self::DeviceTimeout,
            0x0713 => Self::DeviceNoInterfaceQuery,
            0x0714 => Self::DeviceInvalidInterface,
            0x0715 => Self::DeviceInvalidClassId,
            0x0716 => Self::DeviceInvalidObjectId,
            0x0717 => Self::DevicePending,
            0x0718 => Self::DeviceAborted,
            0x0719 => Self::DeviceWarning,
            0x071A => Self::DeviceInvalidArrayIndex,
            0x071B => Self::DeviceSymbolNotActive,
            0x071C => Self::DeviceAccessDenied,
            0x0740 => Self::ClientError,
            0x0741 => Self::ClientInvalidParam,
            0x0742 => Self::ClientListEmpty,
            0x0743 => Self::ClientVarAlreadyInUse,
            0x0744 => Self::ClientInvokeTimeout,
            0x0745 => Self::ClientPortNotOpen,
            0x0746 => Self::ClientNotStarted,
            0x0750 => Self::ClientQueueFull,
            _ => return None,
        })
    }

    /// 数值 (与 C# AoEResultCode 一致).
    pub fn value(self) -> u32 { self as u32 }
}

// ===================== AoE 实例 =====================

/// AoE 实例 - 为每个从站提供 ADS over EtherCAT 功能
///
/// 通过 `Slave::aoe()` 获取实例。
pub struct AoEInstance {
    master_index: u16,
    slave_index: u16,
}

impl AoEInstance {
    /// 创建 AoE 实例 (内部使用)
    pub(crate) fn new(master_index: u16, slave_index: u16) -> Self {
        Self { master_index, slave_index }
    }

    /// 主站索引
    pub fn master_index(&self) -> u16 { self.master_index }

    /// 从站索引
    pub fn slave_index(&self) -> u16 { self.slave_index }

    /// 从站是否支持 AoE 邮箱协议 (对齐 C# AoEInstance.IsSupported).
    ///
    /// 读取 SII mbx_proto bit 0 (ECT_MBXPROT_AOE = 0x01) 判断支持情况.
    /// DLL 调用失败时保守返回 true (未知能力视为支持).
    pub fn is_supported(&self) -> bool {
        let proto = unsafe { ffi::GetSlaveMailboxProto(self.master_index, self.slave_index) };
        (proto & 0x01) != 0
    }

    /// ADS 读写 (可同时发送写入数据)
    ///
    /// `read_length` == 0 表示纯写入; `write_data` 为空表示纯读取。
    pub fn read_write(
        &self,
        index_group: u32,
        index_offset: u32,
        read_length: u32,
        write_data: Option<&[u8]>,
        timeout_us: i32,
    ) -> Result<Option<Vec<u8>>> {
        let write_buf = write_data.unwrap_or(&[]);
        let write_len = write_buf.len() as u32;
        let mut read_ptr: *mut c_void = std::ptr::null_mut();
        let mut bytes_read: u32 = 0;

        let ret = unsafe {
            ffi::AOEReadWrite(
                self.master_index, self.slave_index,
                index_group, index_offset,
                read_length, write_len, write_buf.as_ptr(),
                &mut read_ptr, &mut bytes_read, timeout_us,
            )
        };

        if ret != 0 {
            if !read_ptr.is_null() && bytes_read > 0 {
                let mut data = vec![0u8; bytes_read as usize];
                unsafe {
                    std::ptr::copy_nonoverlapping(read_ptr as *const u8, data.as_mut_ptr(), bytes_read as usize);
                    ffi::FreeMemory(read_ptr);
                }
                Ok(Some(data))
            } else {
                if !read_ptr.is_null() { unsafe { ffi::FreeMemory(read_ptr) }; }
                Ok(None)
            }
        } else {
            Err(DarraError::AoeFailed)
        }
    }

    /// 便捷读操作
    pub fn read(&self, index_group: u32, index_offset: u32, length: u32) -> Result<Option<Vec<u8>>> {
        self.read_write(index_group, index_offset, length, None, DEFAULT_TIMEOUT_US)
    }

    /// 便捷读操作 (带超时)
    pub fn read_with_timeout(&self, index_group: u32, index_offset: u32, length: u32, timeout_us: i32) -> Result<Option<Vec<u8>>> {
        self.read_write(index_group, index_offset, length, None, timeout_us)
    }

    /// 便捷写操作
    pub fn write(&self, index_group: u32, index_offset: u32, data: &[u8]) -> Result<()> {
        let mut read_ptr: *mut c_void = std::ptr::null_mut();
        let mut bytes_read: u32 = 0;
        let ret = unsafe {
            ffi::AOEReadWrite(
                self.master_index, self.slave_index,
                index_group, index_offset,
                0, data.len() as u32, data.as_ptr(),
                &mut read_ptr, &mut bytes_read, DEFAULT_TIMEOUT_US,
            )
        };
        if !read_ptr.is_null() { unsafe { ffi::FreeMemory(read_ptr) }; }
        if ret != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
    }

    /// 发送 ADS 命令并返回响应
    pub fn send_command(
        &self,
        target_port: u16,
        command_id: u16,
        command_data: Option<&[u8]>,
        timeout_us: i32,
    ) -> Result<(bool, Option<Vec<u8>>)> {
        let cmd_data = command_data.unwrap_or(&[]);
        let cmd_size = cmd_data.len() as u32;
        let mut response_ptr: *mut c_void = std::ptr::null_mut();
        let mut response_size: u32 = 0;

        let ret = unsafe {
            ffi::AOESendCommand(
                self.master_index, self.slave_index,
                target_port, command_id,
                cmd_data.as_ptr(), cmd_size,
                &mut response_ptr, &mut response_size, timeout_us,
            )
        };

        if ret != 0 {
            if !response_ptr.is_null() && response_size > 0 {
                let mut data = vec![0u8; response_size as usize];
                unsafe {
                    std::ptr::copy_nonoverlapping(response_ptr as *const u8, data.as_mut_ptr(), response_size as usize);
                    ffi::FreeMemory(response_ptr);
                }
                Ok((true, Some(data)))
            } else {
                if !response_ptr.is_null() { unsafe { ffi::FreeMemory(response_ptr) }; }
                Ok((true, None))
            }
        } else {
            Ok((false, None))
        }
    }

    /// 读取设备信息 (ADS Command 0x01)
    pub fn read_device_info(&self) -> Result<(u8, u8, u16, String)> {
        let mut major_ver: u8 = 0;
        let mut minor_ver: u8 = 0;
        let mut build: u16 = 0;
        let mut name_buf = vec![0u8; 256];
        let ret = unsafe {
            ffi::AOEReadDeviceInfo(
                self.master_index, self.slave_index,
                &mut major_ver, &mut minor_ver, &mut build,
                name_buf.as_mut_ptr(), name_buf.len() as i32,
                DEFAULT_TIMEOUT_US,
            )
        };
        if ret != 0 {
            let null_pos = name_buf.iter().position(|&b| b == 0).unwrap_or(name_buf.len());
            // [2026-05-08 修复乱码] AoE 设备名走多编码兜底 (UTF-8/ASCII/Latin-1)
            let name = crate::utils::help::decode_ethercat_string(&name_buf[..null_pos])
                .trim()
                .to_string();
            Ok((major_ver, minor_ver, build, name))
        } else {
            Err(DarraError::AoeFailed)
        }
    }

    /// 读取 ADS 状态 (Command 0x04)
    pub fn read_state(&self) -> Result<(u16, u16)> {
        let mut ads_state: u16 = 0;
        let mut device_state: u16 = 0;
        let ret = unsafe {
            ffi::AOEReadState(
                self.master_index, self.slave_index,
                &mut ads_state, &mut device_state, DEFAULT_TIMEOUT_US,
            )
        };
        if ret != 0 { Ok((ads_state, device_state)) } else { Err(DarraError::AoeFailed) }
    }

    /// 写入控制命令 (Command 0x05)
    pub fn write_control(&self, ads_state: u16, device_state: u16, data: Option<&[u8]>) -> Result<()> {
        let write_data = data.unwrap_or(&[]);
        let ret = unsafe {
            ffi::AOEWriteControl(
                self.master_index, self.slave_index,
                ads_state, device_state,
                write_data.as_ptr(), write_data.len() as i32,
                DEFAULT_TIMEOUT_US,
            )
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
    }

    /// 添加设备通知 (Command 0x06)
    pub fn add_notification(
        &self, index_group: u32, index_offset: u32, length: u32,
        trans_mode: u32, max_delay: u32, cycle_time: u32,
    ) -> Result<u32> {
        let mut handle: u32 = 0;
        let ret = unsafe {
            ffi::AOEAddNotification(
                self.master_index, self.slave_index,
                index_group, index_offset, length,
                trans_mode, max_delay, cycle_time,
                &mut handle, DEFAULT_TIMEOUT_US,
            )
        };
        if ret != 0 { Ok(handle) } else { Err(DarraError::AoeFailed) }
    }

    /// 删除设备通知 (Command 0x07)
    pub fn del_notification(&self, handle: u32) -> Result<()> {
        let ret = unsafe {
            ffi::AOEDelNotification(self.master_index, self.slave_index, handle, DEFAULT_TIMEOUT_US)
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
    }

    /// 设置 AoE 配置 (NetId 和 Port)
    pub fn set_config(
        &self, target_net_id: &[u8; 6], target_port: u16,
        source_net_id: &[u8; 6], source_port: u16,
    ) -> Result<()> {
        let ret = unsafe {
            ffi::AOESetConfig(
                self.master_index, self.slave_index,
                target_net_id.as_ptr(), target_port,
                source_net_id.as_ptr(), source_port,
            )
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
    }

    /// 获取 AoE 配置 (NetId 和 Port)
    pub fn config(&self) -> Result<([u8; 6], u16, [u8; 6], u16)> {
        let mut target_net_id = [0u8; 6];
        let mut target_port: u16 = 0;
        let mut source_net_id = [0u8; 6];
        let mut source_port: u16 = 0;
        let ret = unsafe {
            ffi::AOEGetConfig(
                self.master_index, self.slave_index,
                target_net_id.as_mut_ptr(), &mut target_port,
                source_net_id.as_mut_ptr(), &mut source_port,
            )
        };
        if ret != 0 { Ok((target_net_id, target_port, source_net_id, source_port)) }
        else { Err(DarraError::AoeFailed) }
    }

    // ===================== 邮箱协议路由常量 (ETG.1020) =====================

    /// CoE (CANopen over EtherCAT) 路由 IndexGroup
    const COE_INDEX_GROUP: u32 = 0xF302;
    /// SoE (Servo over EtherCAT) 路由 IndexGroup
    const SOE_INDEX_GROUP: u32 = 0xF420;

    // ===================== 邮箱协议路由便捷方法 =====================

    /// 通过 AoE 路由读取 CoE 对象 (IndexGroup=0xF302)
    /// IndexOffset 编码: (index << 16) | subindex
    pub fn read_coe_via_aoe(&self, index: u16, subindex: u8, read_length: u32) -> Result<Option<Vec<u8>>> {
        let index_offset = ((index as u32) << 16) | (subindex as u32);
        self.read_write(Self::COE_INDEX_GROUP, index_offset, read_length, None, DEFAULT_TIMEOUT_US)
    }

    /// 通过 AoE 路由写入 CoE 对象 (IndexGroup=0xF302)
    /// IndexOffset 编码: (index << 16) | subindex
    pub fn write_coe_via_aoe(&self, index: u16, subindex: u8, data: &[u8]) -> Result<()> {
        let index_offset = ((index as u32) << 16) | (subindex as u32);
        let mut read_ptr: *mut c_void = std::ptr::null_mut();
        let mut bytes_read: u32 = 0;
        let ret = unsafe {
            ffi::AOEReadWrite(
                self.master_index, self.slave_index,
                Self::COE_INDEX_GROUP, index_offset,
                0, data.len() as u32, data.as_ptr(),
                &mut read_ptr, &mut bytes_read, DEFAULT_TIMEOUT_US,
            )
        };
        if !read_ptr.is_null() { unsafe { ffi::FreeMemory(read_ptr) }; }
        if ret != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
    }

    /// 通过 AoE 路由读取 SoE IDN (IndexGroup=0xF420)
    /// IndexOffset 编码: IDN 编号
    pub fn read_soe_via_aoe(&self, idn: u32, read_length: u32) -> Result<Option<Vec<u8>>> {
        self.read_write(Self::SOE_INDEX_GROUP, idn, read_length, None, DEFAULT_TIMEOUT_US)
    }

    /// 通过 AoE 路由写入 SoE IDN (IndexGroup=0xF420)
    /// IndexOffset 编码: IDN 编号
    pub fn write_soe_via_aoe(&self, idn: u32, data: &[u8]) -> Result<()> {
        let mut read_ptr: *mut c_void = std::ptr::null_mut();
        let mut bytes_read: u32 = 0;
        let ret = unsafe {
            ffi::AOEReadWrite(
                self.master_index, self.slave_index,
                Self::SOE_INDEX_GROUP, idn,
                0, data.len() as u32, data.as_ptr(),
                &mut read_ptr, &mut bytes_read, DEFAULT_TIMEOUT_US,
            )
        };
        if !read_ptr.is_null() { unsafe { ffi::FreeMemory(read_ptr) }; }
        if ret != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
    }

    /// 初始化从站 AoE Net ID (ETG.1020 §9.4)
    /// 在 IP→PreOp 状态切换期间调用，将 Net ID 写入从站 ADS 路由表。
    /// 写入 IndexGroup=1, IndexOffset=3。
    pub fn initialize_slave_net_id(&self, net_id: &[u8; 6]) -> Result<()> {
        self.write(1, 3, net_id)
    }

    /// 获取 ADS 状态名称
    pub fn ads_state_name(ads_state: u16) -> &'static str {
        match ads_state {
            0 => "Invalid", 1 => "Idle", 2 => "Reset", 3 => "Init",
            4 => "Start", 5 => "Run", 6 => "Stop", 7 => "SaveCfg",
            8 => "LoadCfg", 9 => "PowerFailure", 10 => "PowerGood",
            11 => "Error", 12 => "Shutdown", 13 => "Suspend",
            14 => "Resume", 15 => "Config", 16 => "ReConfig",
            _ => "Unknown",
        }
    }
}

// ===================== 订阅管理器 (原 aoe_advanced.rs) =====================

/// AoE 通知传输模式
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum AoeTransMode {
    /// 无通知
    NoTransmission = 0,
    /// 循环通知
    Cyclic = 1,
    /// 变化通知
    OnChange = 2,
    /// 设备端循环通知
    CyclicInDevice = 3,
    /// 设备端变化通知
    OnChangeInDevice = 4,
}

/// AoE 订阅配置
#[derive(Debug, Clone)]
pub struct AoeSubscription {
    /// 从站索引
    pub slave_index: u16,
    /// ADS 索引组
    pub index_group: u32,
    /// ADS 索引偏移
    pub index_offset: u32,
    /// 监听数据长度 (字节)
    pub length: u32,
    /// 传输模式
    pub trans_mode: AoeTransMode,
    /// 最大延迟 (微秒)
    pub max_delay_us: u32,
    /// 周期时间 (微秒)
    pub cycle_time_us: u32,
    /// 超时 (微秒)
    pub timeout_us: i32,
}

impl AoeSubscription {
    /// 创建变化通知订阅
    pub fn on_change(
        slave_index: u16, index_group: u32, index_offset: u32,
        length: u32, timeout_us: i32,
    ) -> Self {
        Self {
            slave_index, index_group, index_offset, length,
            trans_mode: AoeTransMode::OnChange,
            max_delay_us: 1000, cycle_time_us: 0, timeout_us,
        }
    }

    /// 创建循环通知订阅
    pub fn cyclic(
        slave_index: u16, index_group: u32, index_offset: u32,
        length: u32, cycle_time_us: u32, timeout_us: i32,
    ) -> Self {
        Self {
            slave_index, index_group, index_offset, length,
            trans_mode: AoeTransMode::Cyclic,
            max_delay_us: cycle_time_us, cycle_time_us, timeout_us,
        }
    }
}

/// 已激活的订阅句柄
#[derive(Debug)]
struct ActiveSubscription {
    slave_index: u16,
    aoe_handle: u32,
    subscription_index: i32,
    timeout_us: i32,
    config: AoeSubscription,
}

/// AoE 订阅管理器 - 多订阅管理，Drop 时自动清理
pub struct AoeSubscriptionManager {
    master_index: u16,
    subscriptions: HashMap<String, ActiveSubscription>,
    next_id: u32,
}

impl AoeSubscriptionManager {
    /// 创建订阅管理器
    pub fn new(master_index: u16) -> Self {
        Self { master_index, subscriptions: HashMap::new(), next_id: 1 }
    }

    /// 注册订阅
    pub fn subscribe(
        &mut self, name: impl Into<String>, config: AoeSubscription,
        callback: AOENotificationCallback, user_data: *mut std::ffi::c_void,
    ) -> Result<u32> {
        let name = name.into();
        if self.subscriptions.contains_key(&name) {
            return Err(DarraError::Other(format!("订阅 '{}' 已存在", name)));
        }

        let mut aoe_handle: u32 = 0;
        let add_ok = unsafe {
            ffi::AOEAddNotification(
                self.master_index, config.slave_index,
                config.index_group, config.index_offset, config.length,
                config.trans_mode as u32,
                config.max_delay_us, config.cycle_time_us,
                &mut aoe_handle, config.timeout_us,
            )
        };
        if add_ok == 0 { return Err(DarraError::AoeFailed); }

        let sub_idx = unsafe {
            ffi::AOERegisterNotification(
                config.slave_index, aoe_handle,
                config.index_group, config.index_offset, config.length,
                callback, user_data,
            )
        };
        if sub_idx < 0 {
            unsafe { ffi::AOEDelNotification(self.master_index, config.slave_index, aoe_handle, config.timeout_us); }
            return Err(DarraError::AoeFailed);
        }

        let id = self.next_id;
        self.next_id += 1;
        self.subscriptions.insert(name, ActiveSubscription {
            slave_index: config.slave_index, aoe_handle,
            subscription_index: sub_idx, timeout_us: config.timeout_us, config,
        });
        Ok(id)
    }

    /// 注销订阅
    pub fn unsubscribe(&mut self, name: &str) -> Result<()> {
        let sub = self.subscriptions.remove(name)
            .ok_or_else(|| DarraError::Other(format!("订阅 '{}' 不存在", name)))?;
        unsafe { ffi::AOEUnregisterNotification(sub.subscription_index) };
        let del_ok = unsafe {
            ffi::AOEDelNotification(self.master_index, sub.slave_index, sub.aoe_handle, sub.timeout_us)
        };
        if del_ok == 0 { Err(DarraError::AoeFailed) } else { Ok(()) }
    }

    /// 注销所有订阅
    pub fn unsubscribe_all(&mut self) {
        let names: Vec<String> = self.subscriptions.keys().cloned().collect();
        for name in names { let _ = self.unsubscribe(&name); }
    }

    /// 订阅是否存在
    pub fn has_subscription(&self, name: &str) -> bool { self.subscriptions.contains_key(name) }

    /// 活动订阅数量
    pub fn subscription_count(&self) -> usize { self.subscriptions.len() }

    /// 所有订阅名称
    pub fn subscription_names(&self) -> Vec<&str> { self.subscriptions.keys().map(|s| s.as_str()).collect() }

    /// 获取订阅配置
    pub fn subscription_config(&self, name: &str) -> Option<&AoeSubscription> {
        self.subscriptions.get(name).map(|s| &s.config)
    }

    /// 获取订阅句柄
    pub fn subscription_handle(&self, name: &str) -> Option<u32> {
        self.subscriptions.get(name).map(|s| s.aoe_handle)
    }
}

impl Drop for AoeSubscriptionManager {
    fn drop(&mut self) { self.unsubscribe_all(); }
}

impl std::fmt::Debug for AoeSubscriptionManager {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("AoeSubscriptionManager")
            .field("master_index", &self.master_index)
            .field("active_subscriptions", &self.subscriptions.len())
            .finish()
    }
}

// ===================== AoE 辅助函数 =====================

/// 获取所有活跃的 AoE 订阅名称列表
///
/// 对应 C# GetActiveSubscriptions
impl AoeSubscriptionManager {
    pub fn active_subscriptions(&self) -> Vec<&str> {
        self.subscriptions.keys().map(|k| k.as_str()).collect()
    }
}

/// 获取 ADS 状态码的描述字符串
///
/// 对应 C# GetAdsStateDescription
pub fn ads_state_description(ads_state: u16) -> &'static str {
    match ads_state {
        0 => "无效",
        1 => "空闲",
        2 => "复位",
        3 => "初始化",
        4 => "启动",
        5 => "运行",
        6 => "停止",
        7 => "保存配置",
        8 => "加载配置",
        9 => "关机",
        10 => "暂停",
        11 => "重新配置",
        15 => "错误",
        _ => "未知状态",
    }
}

// ===================== 异步 API (轨道 1: std::thread, 无依赖) =====================

impl AoEInstance {
    /// ADS 读 (std::thread 异步包装)
    pub fn read_blocking(
        master_index: u16,
        slave_index: u16,
        index_group: u32,
        index_offset: u32,
        length: u32,
    ) -> std::thread::JoinHandle<Result<Option<Vec<u8>>>> {
        std::thread::spawn(move || {
            let aoe = AoEInstance::new(master_index, slave_index);
            aoe.read(index_group, index_offset, length)
        })
    }

    /// ADS 写 (std::thread 异步包装)
    pub fn write_blocking(
        master_index: u16,
        slave_index: u16,
        index_group: u32,
        index_offset: u32,
        data: Vec<u8>,
    ) -> std::thread::JoinHandle<Result<()>> {
        std::thread::spawn(move || {
            let aoe = AoEInstance::new(master_index, slave_index);
            aoe.write(index_group, index_offset, &data)
        })
    }
}

// ===================== 异步 API (轨道 2: tokio, feature 门控) =====================

#[cfg(feature = "async-tokio")]
impl AoEInstance {
    /// ADS 读 (tokio async)
    pub async fn read_async(
        &self,
        index_group: u32,
        index_offset: u32,
        length: u32,
    ) -> Result<Option<Vec<u8>>> {
        let master = self.master_index;
        let slave = self.slave_index;
        tokio::task::spawn_blocking(move || {
            let aoe = AoEInstance::new(master, slave);
            aoe.read(index_group, index_offset, length)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }

    /// ADS 写 (tokio async)
    pub async fn write_async(
        &self,
        index_group: u32,
        index_offset: u32,
        data: Vec<u8>,
    ) -> Result<()> {
        let master = self.master_index;
        let slave = self.slave_index;
        tokio::task::spawn_blocking(move || {
            let aoe = AoEInstance::new(master, slave);
            aoe.write(index_group, index_offset, &data)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }
}

// ===================== IMailboxProtocol trait impl (对齐 C# AoEInstance) =====================

impl crate::abstractions::MailboxProtocol for AoEInstance {
    fn protocol_type(&self) -> u8 { 0x01 }
    fn protocol_name(&self) -> &'static str { "AoE" }

    fn is_supported(&self) -> bool {
        AoEInstance::is_supported(self)
    }

    fn statistics(&self) -> crate::abstractions::MailboxStatistics {
        let mut stats = ffi::EcMbxStatsC::default();
        let rc = unsafe {
            ffi::mbx_get_stats_by_master(
                self.master_index, self.slave_index, 0x01, &mut stats,
            )
        };
        if rc == 1 {
            stats.into()
        } else {
            crate::abstractions::MailboxStatistics::empty()
        }
    }

    fn reset_statistics(&self) {
        unsafe {
            ffi::mbx_reset_stats_by_master(self.master_index, self.slave_index, 0x01);
        }
    }
}
