using Ayay.SerilogLogs;
using OpenCvSharp;
using Serilog;

namespace SHH.CameraSdk
{
    /// <summary>
    /// [终极版] 动态窗口管理器
    /// 核心修复：
    /// 1.  防止手动关闭窗口后，下一帧自动创建不可缩放的僵尸窗口
    /// 2.  确保关闭时流控策略 (FPS=0) 正确下发，释放帧调度资源
    /// 3.  新增鼠标点击暂停/恢复功能，支持单窗口帧流控制
    /// 4.  【关键修复】在 UILoop 中增加独立轮询机制，**暂停状态下也能检测窗口关闭**
    /// 核心特性：
    /// - 单线程UI渲染：规避 OpenCV GUI 多线程操作风险
    /// - 策略强联动：窗口生命周期与帧控制器订阅状态深度绑定
    /// - 资源闭环：自动清理订阅、销毁窗口、释放 Mat 内存，无内存泄漏
    /// </summary>
    public class DisplayWindowManager : IDisposable
    {
        #region --- 内部窗口上下文类 ---

        private ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);

        /// <summary>
        /// 单个窗口的上下文信息载体
        /// 存储设备关联、运行状态、回调函数等核心数据
        /// </summary>
        private class WindowContext
        {
            /// <summary> 关联的设备ID </summary>
            public int DeviceId { get; set; }
            /// <summary> 窗口暂停状态（volatile 保证多线程可见性） </summary>
            public volatile bool IsPaused;

            /// <summary> 鼠标事件回调函数 </summary>
            public MouseCallback MouseHandler;
            /// <summary> 窗口是否已实际创建（防止重复初始化） </summary>
            public bool IsWindowCreated;
        }
        #endregion

        #region --- 私有成员变量 ---
        /// <summary> 活跃窗口注册表（线程安全） </summary>
        /// <remarks> Key = 窗口标识 AppId, Value = 窗口上下文 </remarks>
        private readonly ConcurrentDictionary<string, WindowContext> _activeWindows = new();

        /// <summary> UI操作指令队列 </summary>
        /// <remarks> 容量限制100，防止渲染阻塞导致内存溢出 </remarks>
        private readonly BlockingCollection<Action> _uiActionQueue = new BlockingCollection<Action>(100);

        /// <summary> UI渲染专属线程（长驻后台） </summary>
        private readonly Task _uiThread;

        /// <summary> 全局取消令牌源：用于优雅终止渲染循环 </summary>
        private readonly CancellationTokenSource _cts = new CancellationTokenSource();

        /// <summary> 相机管理器实例：用于联动帧控制器的流控策略 </summary>
        private readonly CameraManager _cameraManager;
        #endregion

        #region --- 构造与析构函数 ---
        /// <summary>
        /// 初始化动态窗口管理器
        /// </summary>
        /// <param name="cameraManager">相机管理器实例，用于帧策略联动</param>
        public DisplayWindowManager(CameraManager cameraManager)
        {
            _cameraManager = cameraManager;
            // 启动长驻UI线程，设置 LongRunning 提升调度优先级
            _uiThread = Task.Factory.StartNew(UILoop, TaskCreationOptions.LongRunning);
            _sysLog.Information("[DisplayManager] 渲染引擎就绪 (防僵尸窗口终极版)");
        }

        /// <summary>
        /// 释放所有资源，优雅终止窗口管理器
        /// </summary>
        public void Dispose()
        {
            // 1. 发送取消信号，终止 UI 循环
            _cts.Cancel();

            // 2. 等待 UI 线程退出（最多等待1秒，防止卡死）
            try { _uiThread.Wait(1000); }
            catch (Exception ex) { _sysLog.Error($"[DisplayManager] 线程退出异常: {ex.Message}"); }

            // 3. 强制清理所有活跃窗口
            foreach (var appId in _activeWindows.Keys)
            {
                StopDisplay(appId);
            }

            // 4. 清空 UI 指令队列，释放残留资源
            while (_uiActionQueue.TryTake(out var _)) { }

            // 5. 销毁所有 OpenCV 残留窗口
            Cv2.DestroyAllWindows();

            // 6. 释放核心组件资源
            _uiActionQueue.Dispose();
            _cts.Dispose();

            _sysLog.Information("[DisplayManager] 渲染引擎已安全销毁");
        }
        #endregion

        #region --- 窗口生命周期管理 ---
        /// <summary>
        /// 启动指定设备的本地显示窗口
        /// </summary>
        /// <param name="appId">窗口唯一标识</param>
        /// <param name="deviceId">关联的设备ID</param>
        public void StartDisplay(string appId, int deviceId)
        {
            // 防重入：已存在该窗口则直接返回
            if (_activeWindows.ContainsKey(appId)) return;

            _sysLog.Information($"[DisplayManager] 正在启动窗口: {appId} -> Device {deviceId}");

            // 初始化窗口上下文
            var context = new WindowContext
            {
                DeviceId = deviceId,
                IsPaused = false,
                IsWindowCreated = false
            };

            // 注册鼠标左键回调：切换暂停/恢复状态
            context.MouseHandler = (mouseEvent, x, y, flags, userData) =>
            {
                if (mouseEvent == MouseEventTypes.LButtonDown)
                {
                    context.IsPaused = !context.IsPaused;
                    _sysLog.Information($"[DisplayManager] 窗口 {appId} 状态切换: {(context.IsPaused ? "暂停" : "恢复")}");
                }
            };

            // 线程安全地添加窗口上下文
            if (_activeWindows.TryAdd(appId, context))
            {
                // 步骤1：投递窗口初始化指令到 UI 线程
                _uiActionQueue.TryAdd(() =>
                {
                    try
                    {
                        // 创建可缩放窗口，并设置初始尺寸 640x360
                        Cv2.NamedWindow(appId, WindowFlags.Normal | WindowFlags.GuiExpanded);
                        Cv2.ResizeWindow(appId, 640, 360);
                        // 绑定鼠标事件回调
                        Cv2.SetMouseCallback(appId, context.MouseHandler);
                        // 标记窗口已实际创建
                        context.IsWindowCreated = true;
                    }
                    catch (Exception ex)
                    {
                        _sysLog.Error($"[DisplayManager] 窗口 {appId} 初始化失败: {ex.Message}");
                    }
                });

                // 步骤2：订阅设备帧数据流
                GlobalStreamDispatcher.Subscribe(appId, deviceId, frame =>
                {
                    // 快速判断：取消/暂停/窗口已移除，直接跳过帧处理
                    if (_cts.IsCancellationRequested || context.IsPaused || !_activeWindows.ContainsKey(appId))
                    {
                        return;
                    }

                    // 1. 先检查队列容量 (虽然 BlockingCollection 没有完美的无锁 IsFull，但可以通过 Count 判断)
                    // 这是一个不需要 100% 精确的优化，只要能拦截掉大部分无用功即可
                    if (_uiActionQueue.Count >= 30)
                    {
                        return; // 直接丢弃，不进行克隆，节省 CPU
                    }

                    Mat frameClone = null;
                    try
                    {
                        // 深拷贝帧数据：原帧属于解码线程，必须克隆后移交 UI 线程
                        if (frame.TargetMat != null)
                            frameClone = frame.TargetMat.Clone();
                        else
                            frameClone = frame.InternalMat.Clone();
                    }
                    catch (Exception ex)
                    {
                        _sysLog.Error($"[DisplayManager] 帧克隆失败: {ex.Message}");
                        return;
                    }

                    // 投递帧显示指令到 UI 线程
                    bool enqueued = _uiActionQueue.TryAdd(() =>
                    {
                        try
                        {
                            // 双重检查：窗口上下文是否存在 + 窗口是否已创建
                            if (_activeWindows.TryGetValue(appId, out var ctx) && ctx.IsWindowCreated)
                            {
                                // 最后一道防线：检测窗口是否被手动关闭
                                if (Cv2.GetWindowProperty(appId, WindowPropertyFlags.Visible) < 1.0)
                                {
                                    Task.Run(() => StopDisplay(appId));
                                    return;
                                }
                                // 执行帧显示
                                Cv2.ImShow(appId, frameClone);
                            }
                        }
                        finally
                        {
                            // 强制释放克隆的 Mat 内存，杜绝内存泄漏
                            frameClone?.Dispose();
                        }
                    });

                    // 队列满时的兜底处理：手动释放克隆帧
                    if (!enqueued) frameClone?.Dispose();
                });
            }
        }

        /// <summary>
        /// 停止并销毁指定的显示窗口
        /// </summary>
        /// <param name="appId">窗口唯一标识</param>
        public void StopDisplay(string appId)
        {
            // 从注册表中移除窗口上下文
            if (_activeWindows.TryRemove(appId, out var context))
            {
                _sysLog.Information($"[DisplayManager] 正在清理窗口资源: {appId}");

                // 步骤1：立即取消帧数据流订阅
                GlobalStreamDispatcher.Unsubscribe(appId);

                // 步骤2：投递窗口销毁指令到 UI 线程
                _uiActionQueue.TryAdd(() =>
                {
                    try
                    {
                        Cv2.DestroyWindow(appId);
                    }
                    catch (Exception ex)
                    {
                        _sysLog.Error($"[DisplayManager] 窗口 {appId} 销毁失败: {ex.Message}");
                    }
                });

                // 步骤3：关键操作 - 下发流控策略（FPS=0），释放帧控制器资源
                UpdateDevicePolicy(context.DeviceId, appId, 0);
            }
        }
        #endregion

        #region --- 窗口状态控制 ---
        /// <summary>
        /// 暂停指定窗口的帧显示
        /// </summary>
        /// <param name="appId">窗口唯一标识</param>
        public void PauseDisplay(string appId)
        {
            if (_activeWindows.TryGetValue(appId, out var ctx))
            {
                ctx.IsPaused = true;
                _sysLog.Information($"[DisplayManager] 窗口 {appId} 已暂停");
            }
        }

        /// <summary>
        /// 恢复指定窗口的帧显示
        /// </summary>
        /// <param name="appId">窗口唯一标识</param>
        public void ResumeDisplay(string appId)
        {
            if (_activeWindows.TryGetValue(appId, out var ctx))
            {
                ctx.IsPaused = false;
                _sysLog.Information($"[DisplayManager] 窗口 {appId} 已恢复");
            }
        }
        #endregion

        #region --- 流控策略联动 ---
        /// <summary>
        /// 更新设备的帧订阅策略，与帧控制器强联动
        /// </summary>
        /// <param name="deviceId">设备ID</param>
        /// <param name="appId">订阅标识（窗口ID）</param>
        /// <param name="fps">目标帧率（0表示取消订阅）</param>
        private void UpdateDevicePolicy(int deviceId, string appId, int fps)
        {
            try
            {
                // 获取设备实例
                var device = _cameraManager.GetDevice(deviceId);
                if (device == null)
                {
                    _sysLog.Information($"[策略联动] 设备 {deviceId} 不存在");
                    return;
                }

                // 获取设备内置的帧控制器
                var frameController = device.Controller;
                if (frameController == null)
                {
                    _sysLog.Information($"[策略联动] 设备 {deviceId} 未配置帧控制器");
                    return;
                }

                // 根据帧率更新订阅状态
                if (fps > 0)
                {
                    frameController.Register(appId, fps);
                    _sysLog.Information($"[策略联动] ✅ 已注册流控: {appId} -> {fps} FPS");
                }
                else
                {
                    frameController.Unregister(appId);
                    _sysLog.Information($"[策略联动] 🗑️ 已注销流控: {appId}");
                }

                // 记录审计日志，用于前端排查问题
                device.AddAuditLog($"UI播放窗口状态变更: {appId} -> {(fps > 0 ? $"{fps}fps" : "停止")}");
            }
            catch (Exception ex)
            {
                _sysLog.Error($"[策略联动] ❌ 联动失败: {ex.Message}");
            }
        }
        #endregion

        #region --- 核心渲染循环 ---
        /// <summary>
        /// 单线程UI渲染循环，所有 OpenCV GUI 操作的唯一入口
        /// </summary>
        private void UILoop()
        {
            // 轮询计数器：控制窗口状态检查频率
            int checkCounter = 0;

            while (!_cts.IsCancellationRequested)
            {
                try
                {
                    // 阶段1：处理 UI 指令队列
                    int processedCount = 0;
                    // 单次循环最多处理10条指令，避免长时间占用线程
                    while (processedCount < 10 && _uiActionQueue.TryTake(out var action, 5))
                    {
                        action.Invoke();
                        processedCount++;
                    }

                    // 阶段2：执行 OpenCV 消息泵，防止窗口无响应
                    Cv2.WaitKey(1);

                    // 阶段3：【核心修复】独立轮询检测窗口状态
                    // 每20次循环（约200ms）检查一次，不受帧渲染暂停影响
                    checkCounter++;
                    if (checkCounter >= 20)
                    {
                        checkCounter = 0;
                        CheckClosedWindows();
                    }
                }
                catch (Exception ex)
                {
                    _sysLog.Error($"[UI] 渲染循环异常: {ex.Message}");
                }
            }

            // 循环退出时，销毁所有残留窗口
            Cv2.DestroyAllWindows();
        }

        /// <summary>
        /// 检查所有活跃窗口的状态，自动清理被手动关闭的窗口
        /// 【必须在 UI 线程调用】
        /// </summary>
        private void CheckClosedWindows()
        {
            // 获取当前活跃窗口ID的快照，避免遍历过程中集合被修改
            var activeIds = _activeWindows.Keys.ToArray();

            foreach (var appId in activeIds)
            {
                try
                {
                    // GetWindowProperty 必须在 UI 线程执行
                    // Visible < 1.0 表示窗口已被用户手动关闭
                    if (Cv2.GetWindowProperty(appId, WindowPropertyFlags.Visible) < 1.0)
                    {
                        _sysLog.Information($"[UI] 检测到窗口 {appId} 已被手动关闭，触发清理...");
                        // 异步清理：避免 StopDisplay 内部的队列操作阻塞 UI 线程
                        Task.Run(() => StopDisplay(appId));
                    }
                }
                catch
                {
                    // 捕获异常：通常是窗口已不存在，直接触发清理
                    Task.Run(() => StopDisplay(appId));
                }
            }
        }
        #endregion
    }
}