using Serilog;

namespace SHH.CameraSdk;

/// <summary>
/// [架构基类] 工业级视频源抽象核心 (V3.5.0 严格匹配版)
/// <para>当前模块: AiVideo | 核心原则: 低耦合、高并发、零拷贝</para>
/// 核心职责：
/// <para>1. 生命周期管理：基于 <see cref="SemaphoreSlim"/> 实现 Start/Stop/UpdateConfig 的线程安全串行化</para>
/// <para>2. 状态分发系统：利用 <see cref="Channel{T}"/> (DropOldest策略) 实现高性能、非阻塞的状态变更通知</para>
/// <para>3. 遥测统计引擎：内置原子级 FPS 计算与 Mbps 带宽监控，支持网络层与解码层双路流量计费</para>
/// <para>4. 弹性自愈机制：集成 IsAuthFailed 冻结期与物理网络 (Ping) 状态同步，支持 Coordinator 级自动重连</para>
/// <para>5. 审计与追踪：内置环形审计日志 (Max:100)，记录配置变更、动态参数应用及驱动层异常</para>
/// 关键修复记录：
/// <para>✅ [V3.3.5] 资源防御：在 DisposeAsync 中强化生命周期锁，防止销毁期间触发重入</para>
/// <para>✅ [V3.3.5] 自愈增强：引入 Environment.TickCount64 宽限期机制，解决启动瞬间心跳超时误判</para>
/// <para>✅ [Bug A] 死锁免疫：强制所有 await 添加 ConfigureAwait(false)，彻底解除对同步上下文的依赖</para>
/// <para>✅ [Bug π] 管道安全：Dispose 采用 TryComplete 优雅关闭策略，确保缓冲区剩余状态消息被完全消费</para>
/// </summary>
public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceConnectivity
{
    #region --- 1. 核心字段与锁机制 (Fields & Locks) ---

    /// <summary>
    /// 核心配置对象（支持热更新）
    /// 注意：外部修改需通过 UpdateConfig 方法，确保线程安全
    /// </summary>
    protected VideoSourceConfig _config;

    /// <summary>状态同步锁：保护 _status 字段的读写原子性，防止多线程竞争导致状态不一致</summary>
    private readonly object _stateSyncRoot = new();

    /// <summary>
    /// 生命周期互斥锁（信号量）
    /// 作用：确保 StartAsync/StopAsync/UpdateConfig 串行执行，防止重入导致状态机混乱
    /// 配置：初始计数 1，最大计数 1 → 互斥锁
    /// </summary>
    private readonly SemaphoreSlim _lifecycleLock = new(1, 1);

    /// <summary> 跟踪上一个未完成的生命周期任务 </summary>
    private Task _pendingLifecycleTask = Task.CompletedTask;

    /// <summary> 物理在线状态（由哨兵 Ping 更新） </summary>
    private volatile bool _isPhysicalOnline;

    /// <summary> 设备在线状态标志（volatile 确保多线程可见性） </summary>
    private volatile bool _isActived;

    /// <summary> 视频源核心状态（受 _stateSyncRoot 保护） </summary>
    private VideoSourceStatus _status = VideoSourceStatus.Disconnected;

    /// <summary> 资源销毁标记 </summary>
    private volatile bool _isDisposed = false;

    #endregion

    #region --- 2. 状态分发基础设施 (Status Channel) ---

    /// <summary>
    /// 状态通知有界通道
    /// 特性：DropOldest 策略，消费者过载时丢弃旧状态，防止内存溢出
    /// 配置：容量 100 | 单读者多写者
    /// </summary>
    private readonly Channel<StatusChangedEventArgs> _statusQueue;

    /// <summary> 状态分发器的取消令牌源 </summary>
    private CancellationTokenSource? _distributorCts;

    /// <summary> 状态分发任务引用（用于 Dispose 时优雅等待） </summary>
    private Task? _distributorTask;

    #endregion

    #region --- 3. 遥测与性能统计 (Telemetry & Stats) ---

    /// <summary> 最后一帧接收的系统 Tick（单调时钟，不受系统时间修改影响） </summary>
    private long _lastFrameTick = 0;

    /// <summary> 生命周期内接收的总帧数 </summary>
    private long _totalFramesReceived = 0;

    /// <summary> FPS 计算临时计数器 </summary>
    private int _tempFrameCounter = 0;

    /// <summary> 上次 FPS 计算的 Tick 时间 </summary>
    private long _lastFpsCalcTick = 0;

    /// <summary> 实时码率 (Mbps) </summary>
    protected double _currentBitrate = 0;

    /// <summary> 码率计算临时字节计数器 </summary>
    private long _tempByteCounter = 0;

    #endregion

    #region --- 4. 审计日志与自愈标记 (Audit & Resilience) ---

    /// <summary> 审计日志列表（线程安全访问） </summary>
    private readonly List<string> _auditLogs = new();

    /// <summary> 最大日志条数（滚动清除，防止内存溢出） </summary>
    private const int MaxAuditLogCount = 100;

    /// <summary> 
    /// 认证类致命错误标记（如密码错、用户锁定）
    /// 作用：触发 15 分钟长冷冻期，防止 IP 被相机锁定
    /// </summary>
    public bool IsAuthFailed { get; set; }

    /// <summary>上次尝试执行 StartAsync 的系统 Tick 时间 (单调时钟)</summary>
    private long _lastStartAttemptTick = 0;

    #endregion

    #region --- 5. 公开事件 (Events) ---

    /// <summary> 视频帧回调事件（热路径，低延迟分发） </summary>
    public event Action<object>? FrameReceived;

    /// <summary> 状态变更事件（对外异步暴露状态通知） </summary>
    public event EventHandler<StatusChangedEventArgs>? StatusChanged;

    #endregion

    #region --- 6. 公开属性 (Properties) ---

    /// <summary> 视频源唯一标识 </summary>
    public long Id => _config.Id;

    /// <summary> 只读配置副本（外部仅能通过 UpdateConfig 修改） </summary>
    public VideoSourceConfig Config => _config;

    /// <summary> 视频源当前状态（线程安全读取） </summary>
    public VideoSourceStatus Status
    {
        get
        {
            lock (_stateSyncRoot) return _status;
        }
    }

    /// <summary> 运行状态标记 </summary>
    public bool IsRunning { get; set; }

    /// <summary> 设备在线状态 </summary>
    public bool IsActived => _isActived;

    /// <summary> 物理在线状态（Ping 结果） </summary>
    public bool IsPhysicalOnline => _isPhysicalOnline;

    /// <summary> 设备元数据（能力集、通道信息等） </summary>
    public DeviceMetadata Metadata { get; protected set; } = new();

    /// <summary> 帧控制器（用于帧分发策略管理） </summary>
    public FrameController Controller { get; protected set; }

    /// <summary>
    /// 图像预处理配置（缩放、增量等）
    /// 放置在基类中，确保所有接入协议（HIK/DH/RTSP）均可共享处理逻辑
    /// </summary>
    public PreprocessConfig PreprocessSettings { get; set; }
        = new PreprocessConfig();

    /// <summary> 最后一帧接收的 Tick 时间戳（线程安全读取） </summary>
    public long LastFrameTick => Interlocked.Read(ref _lastFrameTick);

    /// <summary> 生命周期总帧数（线程安全读取） </summary>
    public long TotalFrames => Interlocked.Read(ref _totalFramesReceived);

    /// <summary> 实时 FPS（每秒更新一次） </summary>
    public double RealFps { get; private set; } = 0.0;

    // 提供一个最近一秒的输入帧率参考值
    public int NominalInputFps => (int)Math.Round(RealFps);

    /// <summary> 实时码率 (Mbps) </summary>
    public double RealBitrate => _currentBitrate;

    /// <summary> 视频宽度 </summary>
    public int Width { get; protected set; }

    /// <summary> 视频高度 </summary>
    public int Height { get; protected set; }

    /// <summary> 满足接口要求的 IP 地址 </summary>
    string IDeviceConnectivity.IpAddress => _config.IpAddress;

    /// <summary> 上次启动尝试的时间戳 </summary>
    public long LastStartAttemptTick => Interlocked.Read(ref _lastStartAttemptTick);

    #endregion

    #region --- 7. 抽象成员 (Abstracts) ---

    /// <summary> 子类特定的日志记录器 </summary>
    protected abstract ILogger _sdkLog { get; }

    /// <summary>
    /// 驱动层启动逻辑（子类必须实现）
    /// 包含：设备登录、码流订阅、取流线程启动等
    /// </summary>
    /// <param name="token">取消令牌</param>
    protected abstract Task OnStartAsync(CancellationToken token);

    /// <summary>
    /// 驱动层停止逻辑（子类必须实现）
    /// 包含：码流停止、设备登出、资源释放等
    /// </summary>
    protected abstract Task OnStopAsync();

    /// <summary>
    /// 驱动层元数据获取逻辑（子类必须实现）
    /// </summary>
    /// <returns>设备元数据</returns>
    protected abstract Task<DeviceMetadata> OnFetchMetadataAsync();

    #endregion

    #region --- 8. 构造函数 (Constructor) ---

    /// <summary>
    /// 初始化视频源基础设施
    /// </summary>
    /// <param name="config">视频源基础配置</param>
    /// <exception cref="ArgumentNullException">配置为空时抛出</exception>
    protected BaseVideoSource(VideoSourceConfig config)
    {
        // 入参校验
        _config = config ?? throw new ArgumentNullException(nameof(config), "视频源配置不能为空");

        // 初始化帧控制器
        Controller = new FrameController();

        // 配置深拷贝（防漂移：内部配置与外部引用隔离）
        _config = CloneConfig(config);

        // 初始化有界状态通道
        _statusQueue = Channel.CreateBounded<StatusChangedEventArgs>(new BoundedChannelOptions(100)
        {
            FullMode = BoundedChannelFullMode.DropOldest,
            SingleReader = true,
            SingleWriter = false
        });

        // 初始化状态分发器
        _distributorCts = new CancellationTokenSource();
        _distributorTask = Task.Run(() => StatusDistributorLoopAsync(_distributorCts.Token));
    }

    #endregion

    #region --- 9. 生命周期控制 (Lifecycle) ---

    /// <summary>
    /// 异步启动设备连接
    /// 包含：状态校验、非托管初始化、元数据刷新
    /// </summary>
    public async Task StartAsync()
    {
        // Optimized: [原因] 增加销毁前置检查，配合 Bug 修复方案
        if (_isDisposed || _lifecycleLock == null)
            throw new ObjectDisposedException(nameof(BaseVideoSource), "设备实例已销毁");

        // 死锁免疫：不捕获当前同步上下文
        await _lifecycleLock.WaitAsync().ConfigureAwait(false);
        try
        {
            // 等待上一个生命周期任务完成
            await _pendingLifecycleTask.ConfigureAwait(false);

            // 幂等性检查：已在线则直接返回
            if (_isActived) return;

            // 更新状态为连接中
            UpdateStatus(VideoSourceStatus.Connecting, $"正在启动 {_config.Brand} 设备...");

            // 驱动层启动逻辑（带 15 秒超时）
            using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
            await OnStartAsync(cts.Token).ConfigureAwait(false);

            // ============================================================
            // 【核心修复位置】
            // 赋予心跳 5 秒的宽限期 (Grace Period)
            // 这样 Coordinator 在未来 5 秒内计算出来的无帧时间将是负数或极小值，不会触发复位
            // ============================================================
            Interlocked.Exchange(ref _lastFrameTick, Environment.TickCount64 + 5000);

            // 标记运行状态
            _isActived = true;
            IsRunning = true;

            // 更新状态为播放中，并刷新元数据
            UpdateStatus(VideoSourceStatus.Playing, "流传输正常运行");
            await RefreshMetadataAsync().ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            // 异常回滚：标记离线并更新状态
            _isActived = false;
            UpdateStatus(VideoSourceStatus.Disconnected, $"启动失败: {ex.Message}");
            throw; // 向上抛出异常，由上层处理
        }
        finally
        {
            _lifecycleLock.Release();
        }
    }

    /// <summary>
    /// 异步停止设备连接
    /// </summary>
    public async Task StopAsync()
    {
        await _lifecycleLock.WaitAsync().ConfigureAwait(false);
        try
        {
            // 标记离线，阻断后续数据处理
            _isActived = false;

            // 执行驱动层停止逻辑
            await OnStopAsync().ConfigureAwait(false);
        }
        finally
        {
            // 更新状态并释放锁
            UpdateStatus(VideoSourceStatus.Disconnected, "连接已手动断开");
            _lifecycleLock.Release();
        }
    }

    #endregion

    #region --- 10. 配置与元数据管理 (Config & Metadata) ---

    /// <summary>
    /// 热更新视频源配置（线程安全）
    /// 新配置将在下次启动/重连时生效
    /// </summary>
    /// <param name="newConfig">新的视频源配置</param>
    public void UpdateConfig(VideoSourceConfig newConfig)
    {
        if (newConfig == null) return;

        // 加生命周期锁：防止与启动/停止操作并发
        _lifecycleLock.Wait();
        try
        {
            // 深拷贝新配置，隔离外部引用
            _config = newConfig.DeepCopy();

            // 写入审计日志
            AddAuditLog($"配置已更新 [IP:{_config.IpAddress}]，生效时机：{(_isActived ? "下次重连" : "下次启动")}");
            Debug.WriteLine($"[ConfigUpdated] 设备 {Id} 配置落地完成");
        }
        finally
        {
            _lifecycleLock.Release();
        }
    }

    /// <summary>
    /// 配置深拷贝辅助方法（确保引用类型独立）
    /// </summary>
    /// <param name="source">源配置</param>
    /// <returns>深拷贝后的新配置</returns>
    private VideoSourceConfig CloneConfig(VideoSourceConfig source)
    {
        return new VideoSourceConfig
        {
            Id = source.Id,
            Brand = source.Brand,
            IpAddress = source.IpAddress,
            Port = source.Port,
            Username = source.Username,
            Password = source.Password,
            ChannelIndex = source.ChannelIndex,
            StreamType = source.StreamType,
            Transport = source.Transport,
            ConnectionTimeoutMs = source.ConnectionTimeoutMs,
            MainboardIp = source.MainboardIp,
            MainboardPort = source.MainboardPort,
            RtspPath = source.RtspPath,
            RenderHandle = source.RenderHandle,
            // Dictionary 深拷贝：防止外部修改影响内部
            VendorArguments = source.VendorArguments != null
                ? new Dictionary<string, string>(source.VendorArguments)
                : new Dictionary<string, string>()
        };
    }

    /// <summary>
    /// 刷新设备元数据，对比差异并更新
    /// </summary>
    /// <returns>元数据差异描述符</returns>
    public async Task<MetadataDiff> RefreshMetadataAsync()
    {
        // 离线状态不刷新元数据
        if (!_isActived) return MetadataDiff.None;

        try
        {
            // 获取驱动层最新元数据
            var latestMetadata = await OnFetchMetadataAsync().ConfigureAwait(false);
            if (latestMetadata != null && latestMetadata.ChannelCount > 0)
            {
                // 对比新旧元数据差异
                var diff = Metadata.CompareWith(latestMetadata);
                // 更新元数据并标记同步时间
                Metadata = latestMetadata;
                Metadata.MarkSynced();
                return diff;
            }
        }
        catch (Exception ex)
        {
            AddAuditLog($"元数据刷新失败: {ex.Message}");
            Debug.WriteLine($"[MetadataWarning] 设备 {Id}: {ex.Message}");
        }

        return MetadataDiff.None;
    }

    /// <summary>
    /// 应用动态流参数（运行时热更新）
    /// 支持：分辨率切换、码流类型切换、渲染句柄变更等
    /// </summary>
    /// <param name="options">动态配置项</param>
    public void ApplyOptions(DynamicStreamOptions options)
    {
        // 离线或参数为空时，忽略请求
        if (options == null || !_isActived)
        {
            AddAuditLog("动态参数应用失败：设备离线或参数为空");
            return;
        }

        try
        {
            // 1. 基于设备能力校验参数合法性
            if (Metadata.ValidateOptions(options, out string errorMsg))
            {
                // 2. 执行驱动层参数应用逻辑
                OnApplyOptions(options);
                UpdateStatus(_status, "动态参数已成功应用");

                // 3. 记录参数变更日志
                var changeLog = new List<string>();
                if (options.StreamType.HasValue) changeLog.Add($"码流类型={options.StreamType}");
                if (options.RenderHandle.HasValue) changeLog.Add($"渲染句柄已更新");
                if (options.TargetFps.HasValue) changeLog.Add($"分析帧率={options.TargetFps}fps");

                AddAuditLog($"动态参数应用: {string.Join(" | ", changeLog)}");
            }
            else
            {
                Debug.WriteLine($"[OptionRejected] 设备 {Id}: {errorMsg}");
            }
        }
        catch (Exception ex)
        {
            AddAuditLog($"动态参数应用失败: {ex.Message}");
            Debug.WriteLine($"[ApplyOptionsError] {ex.Message}");
        }
    }

    /// <summary>
    /// 驱动层参数应用逻辑（子类重写）
    /// </summary>
    /// <param name="options">动态配置项</param>
    protected virtual void OnApplyOptions(DynamicStreamOptions options) { }

    #endregion

    #region --- 11. 帧处理与遥测上报 (Data Handling) ---

    /// <summary>
    /// 检查是否存在帧订阅者（性能优化）
    /// 无订阅时可跳过解码/预处理等耗时操作
    /// </summary>
    /// <returns>有订阅者返回 true</returns>
    protected bool HasFrameSubscribers() => FrameReceived != null;

    /// <summary>
    /// 触发帧回调事件（热路径优化）
    /// </summary>
    /// <param name="frameData">帧数据（如 Mat/SmartFrame）</param>
    protected void RaiseFrameReceived(object frameData) => FrameReceived?.Invoke(frameData);

    /// <summary>
    /// 标记数据接收（心跳保活 + 双路统计）
    /// <para>调用规则：</para>
    /// <para>1. 网络层收到流数据时：调用 MarkFrameReceived(dwBufSize)，只统计流量。</para>
    /// <para>2. 解码层流控通过后：调用 MarkFrameReceived(0)，只统计有效帧率。</para>
    /// </summary>
    /// <param name="dataSize">数据包大小（字节），0 表示这是一帧解码后的图像</param>
    protected void MarkFrameReceived(uint dataSize = 0)
    {
        long now = Environment.TickCount64;

        // 1. [心跳保活] 无论网络包还是解码帧，都视为设备“活着”
        // 使用 Interlocked 保证多线程读写安全
        Interlocked.Exchange(ref _lastFrameTick, now);

        // 2. [分流累加] 根据来源不同，累加不同的计数器
        if (dataSize > 0)
        {
            // --- 来源：网络层回调 (SafeOnRealDataReceived) ---
            // 只累加字节数，用于计算带宽 (Mbps)
            // 绝对不能在这里累加帧数，否则会被网络包的数量误导（导致 FPS 虚高）
            Interlocked.Add(ref _tempByteCounter, dataSize);
        }
        else
        {
            // --- 来源：解码层回调 (SafeOnDecodingCallBack) ---
            // 只累加帧数，用于计算有效 FPS
            // 只有经过 MakeDecision() 筛选保留下来的帧才走到这里，所以是真实的 "Output FPS"
            Interlocked.Increment(ref _tempFrameCounter);

            // 累加生命周期总帧数
            Interlocked.Increment(ref _totalFramesReceived);
        }

        // 3. [定期结算] 每 1000ms (1秒) 结算一次统计指标
        long timeDiff = now - _lastFpsCalcTick;
        if (timeDiff >= 1000)
        {
            // 忽略第一次冷启动的数据（避免除以 0 或时间跨度过大）
            if (_lastFpsCalcTick > 0)
            {
                double duration = timeDiff / 1000.0;

                // --- A. 结算有效帧率 (FPS) ---
                // 原子读取并重置计数器，防止漏算
                int frames = Interlocked.Exchange(ref _tempFrameCounter, 0);
                RealFps = Math.Round(frames / duration, 1);

                // --- B. 结算网络带宽 (Mbps) ---
                // 公式: (字节数 * 8位) / 1024 / 1024 / 秒数
                long bytes = Interlocked.Exchange(ref _tempByteCounter, 0);
                _currentBitrate = Math.Round((bytes * 8.0) / 1048576.0 / duration, 2);
            }
            else
            {
                // 初始化重置：确保原子性
                Interlocked.Exchange(ref _tempFrameCounter, 0);
                Interlocked.Exchange(ref _tempByteCounter, 0);
            }

            // 更新结算时间锚点
            _lastFpsCalcTick = now;
        }
    }

    #endregion

    #region --- 12. 状态管理与分发 (Status Distributor) ---

    /// <summary>
    /// 更新设备状态并写入分发队列
    /// </summary>
    /// <param name="status">新状态</param>
    /// <param name="msg">状态描述</param>
    /// <param name="ex">关联异常（可选）</param>
    protected void UpdateStatus(VideoSourceStatus status, string msg, CameraException? ex = null)
    {
        lock (_stateSyncRoot)
        {
            // 更新内部状态
            _status = status;

            // 写入状态队列（满时自动丢弃旧数据）
            _ = _statusQueue.Writer.TryWrite(new StatusChangedEventArgs(
                status,
                msg,
                ex,
                ex?.RawErrorCode)
            {
                NewHandle = ex?.Context.TryGetValue("NativeHandle", out var handle) == true ? (IntPtr)handle : null
            });
        }
    }

    /// <summary>
    /// 允许哨兵从外部更新 _isOnline 字段
    /// </summary>
    /// <param name="isOnline"></param>
    void IDeviceConnectivity.SetNetworkStatus(bool isOnline)
    {
        if (_isPhysicalOnline != isOnline)
        {
            _isPhysicalOnline = isOnline;
            // 触发状态变更是为了通知 UI 更新绿色小圆点，但不改变 Status
            // 注意：这里传 _status 保持原样，只变消息
            StatusChanged?.Invoke(this, new StatusChangedEventArgs(_status, isOnline ? "物理网络恢复" : "物理网络中断"));
        }
    }

    /// <summary>
    /// 后台状态分发循环（单线程消费状态队列）
    /// </summary>
    /// <param name="token">取消令牌</param>
    private async Task StatusDistributorLoopAsync(CancellationToken token)
    {
        try
        {
            // 关键修复：使用 CancellationToken.None 等待读取
            // 确保取消时仍能消费完队列剩余消息
            while (await _statusQueue.Reader.WaitToReadAsync(CancellationToken.None).ConfigureAwait(false))
            {
                // 批量读取队列中的所有消息
                while (_statusQueue.Reader.TryRead(out var args))
                {
                    // 异常隔离：捕获订阅者回调异常，防止分发器崩溃
                    try
                    {
                        StatusChanged?.Invoke(this, args);
                    }
                    catch (Exception ex)
                    {
                        _sdkLog.Error(ex, "设备 {Id} 状态变更回调异常", Id);
                    }

                    // 退出条件：取消令牌已触发 且 队列为空
                    if (token.IsCancellationRequested && _statusQueue.Reader.Count == 0)
                    {
                        return;
                    }
                }

                // 双重检查退出条件
                if (token.IsCancellationRequested && _statusQueue.Reader.Count == 0)
                {
                    return;
                }
            }
        }
        catch (OperationCanceledException) { /* 正常退出 */ }
        catch (Exception ex)
        {
            _sdkLog.Fatal(ex, "设备 {Id} 状态分发器致命异常", Id);
        }
        finally
        {
            // Optimized: [原因] 确保在分发器关闭后，缓冲区内残余的状态消息能被强制消费完。
            while (_statusQueue.Reader.TryRead(out var args))
            {
                try { StatusChanged?.Invoke(this, args); } catch { /* 忽略销毁期的回调异常 */ }
            }
        }
    }

    /// <summary>
    /// 上报驱动层异常，触发重连自愈逻辑
    /// </summary>
    /// <param name="ex">相机统一异常</param>
    protected void ReportError(CameraException ex)
    {
        if (!_isActived) return;

        // 标记离线并更新状态为重连中
        _isActived = false;
        UpdateStatus(VideoSourceStatus.Reconnecting, $"SDK异常: {ex.Message}", ex);
    }

    #endregion

    #region --- 13. 自愈与审计辅助 (Resilience & Audit) ---

    /// <summary> 
    /// 更新最后一次启动尝试的时间戳为当前时间
    /// </summary>
    public void MarkStartAttempt()
    {
        Interlocked.Exchange(ref _lastStartAttemptTick, Environment.TickCount64);
    }

    /// <summary>
    /// 强制重置自愈相关的冷却与错误标记
    /// 用于用户手动干预（如修改密码）后，使协调器能立即触发下一次尝试
    /// </summary>
    public void ResetResilience()
    {
        // 1. 清除认证失败标记
        IsAuthFailed = false;

        // 2. 将尝试时间戳归零
        // 这样在 Coordinator 中计算 elapsed = now - 0，结果会远大于 30s
        Interlocked.Exchange(ref _lastStartAttemptTick, 0);

        _sdkLog.Debug($"[Sdk] 设备 {Id} 自愈状态已人工重置");
    }

    /// <summary>
    /// 添加审计日志（线程安全）
    /// </summary>
    /// <param name="message">日志内容</param>
    public void AddAuditLog(string message)
    {
        lock (_auditLogs)
        {
            var logEntry = $"[{DateTime.Now:HH:mm:ss.fff}] {message}";
            _auditLogs.Add(logEntry);

            // 滚动清除旧日志
            if (_auditLogs.Count > MaxAuditLogCount)
            {
                _auditLogs.RemoveAt(0);
            }
        }
    }

    /// <summary>
    /// 获取审计日志副本
    /// </summary>
    /// <returns>日志列表副本</returns>
    public List<string> GetAuditLogs()
    {
        lock (_auditLogs)
        {
            return new List<string>(_auditLogs);
        }
    }

    /// <summary>
    /// 清空审计日志
    /// </summary>
    public void ClearAuditLogs()
    {
        _auditLogs.Clear();
        AddAuditLog("用户清空了审计日志");
    }

    #endregion

    #region --- 14. 资源释放 (Disposal) ---

    /// <summary>
    /// 同步销毁入口（死锁免疫）
    /// </summary>
    public void Dispose()
    {
        // 触发异步销毁，但设定一个超时兜底，防止永久卡死 UI
        // 这里等待 2 秒，如果还没销毁完也强行返回，避免界面冻结
        Task.Run(async () => await DisposeAsync().ConfigureAwait(false))
            .Wait(TimeSpan.FromSeconds(2));

        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 异步销毁资源（优雅关闭）
    /// </summary>
    /// <returns>ValueTask</returns>
    public virtual async ValueTask DisposeAsync()
    {
        // 防止重复 Dispose
        if (_isDisposed) return;

        // 提前锁定并获取引用
        var semaphore = _lifecycleLock;
        if (semaphore == null) return;

        // Optimized: [原因] 获取生命周期锁，防止在 DisposeAsync 执行期间被并发触发 Start/Stop 操作
        await _lifecycleLock.WaitAsync().ConfigureAwait(false);

        try
        {
            // 防止重复 Dispose
            if (_isDisposed) return;

            // 1. 停止业务逻辑
            await StopAsync().ConfigureAwait(false);

            _isDisposed = true;

            // 2. 优雅关闭状态分发器
            _statusQueue.Writer.TryComplete(); // 标记队列不再接受新消息
            _distributorCts?.Cancel();         // 触发分发器取消

            // 3. 等待分发器处理完剩余消息（最多等待 500ms）
            if (_distributorTask != null)
            {
                await Task.WhenAny(_distributorTask, Task.Delay(500)).ConfigureAwait(false);
            }

            // 4. 切断事件引用，防止内存泄漏
            FrameReceived = null;
            StatusChanged = null;

            // 5. 释放基础资源
            _lifecycleLock.Dispose();
            _distributorCts?.Dispose();
        }
        finally
        {
            // Optimized: [原因] 修复信号量销毁顺序。
            // 先释放锁，让可能存在的阻塞线程（虽然被 _isDisposed 阻断）能正常通过，
            // 然后检查是否为销毁流程的最后一步。
            semaphore.Release();

            // 彻底销毁信号量并清空引用，确保后续调用不再访问已释放的对象
            _lifecycleLock?.Dispose();

            // 6. 抑制垃圾回收器的终结器
            GC.SuppressFinalize(this);
        }
    }

    #endregion
}