using Ayay.SerilogLogs;
using Serilog;

namespace SHH.CameraSdk;

/// <summary>
/// [管理层] 视频源总控管理器 (V3.5 持久化集成版)
/// 核心职责：统一管理所有相机设备的生命周期、状态监控与资源清理，对接协调器实现自动自愈
/// </summary>
public class CameraManager : IDisposable, IAsyncDisposable
{
    #region --- 1. 核心资源与状态 (Fields & States) ---

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

    /// <summary> 全局设备实例池（线程安全），Key = 设备唯一标识 </summary>
    private readonly ConcurrentDictionary<long, BaseVideoSource> _cameraPool = new();

    /// <summary> 后台协调器实例：负责心跳检测、断线重连、僵尸流恢复 </summary>
    private readonly CameraCoordinator _coordinator = new();

    /// <summary> 全局取消令牌源：用于销毁时瞬间关停所有异步扫描任务 </summary>
    private readonly CancellationTokenSource _globalCts = new();

    /// <summary> 销毁状态标记：防止重复销毁或销毁过程中执行操作 </summary>
    private volatile bool _isDisposed;

    /// <summary> 
    /// 协调器引擎运行状态标记
    /// 使用 volatile 关键字确保多线程环境下的内存可见性
    /// </summary>
    private volatile bool _isEngineStarted = false;

    #endregion

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

    // [修改] 注入 IStorageService
    public CameraManager()
    {
    }

    #endregion

    #region --- 2. 设备管理 (Device Management) ---

    /// <summary>
    /// 向管理池添加新相机设备
    /// </summary>
    /// <param name="config">相机设备配置信息</param>
    public void AddDevice(VideoSourceConfig config)
    {
        // [安全防护] 销毁过程中禁止添加新设备
        if (_isDisposed) return;

        // 使用工厂方法创建
        var device = CreateDeviceInstance(config);

        if (!_cameraPool.TryAdd(config.Id, device))
        {
            // 如果添加失败（ID冲突），由于 device 还没被使用，直接释放掉
            device.DisposeAsync().AsTask().Wait();

            _sysLog.Warning($"[Core] 设备 ID:{config.Id} 已存在");
            _sysLog.Debug($"[Core] 设备 ID:{config.Id} => 明细:" + "{@cfg}.", config);
            throw new InvalidOperationException($"设备 ID {config.Id} 已存在");
        }

        _coordinator.Register(device);

        // 动态激活逻辑：引擎已启动时，新设备直接标记为运行状态
        if (_isEngineStarted)
            device.IsRunning = true;
    }

    /// <summary>
    /// 根据设备ID获取指定的视频源实例
    /// </summary>
    public BaseVideoSource? GetDevice(long id)
        => _cameraPool.TryGetValue(id, out var source) ? source : null;

    /// <summary>
    /// 获取当前管理的所有相机设备
    /// </summary>
    public IEnumerable<BaseVideoSource> GetAllDevices()
    {
        return _cameraPool.Values.ToList();
    }

    /// <summary>
    /// [修改] 异步移除设备 (从 RemoveDevice 改为 RemoveDeviceAsync)
    /// </summary>
    public async Task RemoveDeviceAsync(long id)
    {
        if (_cameraPool.TryRemove(id, out var device))
        {
            // 记录日志
            _sysLog.Information("[Core] 正在移除设备, ID {0} ", id);

            // 1. 停止物理连接
            await device.StopAsync();

            // 2. 释放资源
            await device.DisposeAsync();

            _sysLog.Warning("[Core] 设备已彻底移除, ID {0} ", id);
        }
    }

    // 为了兼容旧代码保留同步方法，但不推荐使用
    public void RemoveDevice(long id) => RemoveDeviceAsync(id).Wait();

    #endregion

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

    /// <summary>
    /// 启动视频管理引擎，加载配置，初始化SDK并启动协调器自愈循环
    /// </summary>
    public async Task StartAsync()
    {
        if (_isDisposed) throw new ObjectDisposedException(nameof(CameraManager));
        if (_isEngineStarted) return;

        // =========================================================
        // 1. 全局驱动环境预初始化
        // =========================================================
        HikSdkManager.Initialize();
        DahuaSdkManager.Initialize();

        // 标记引擎启动状态
        _isEngineStarted = true;

        // =========================================================
        // 2. 启动协调器后台自愈循环
        // =========================================================
        _ = Task.Factory.StartNew(
            () => _coordinator.RunCoordinationLoopAsync(_globalCts.Token),
            _globalCts.Token,
            TaskCreationOptions.LongRunning,
            TaskScheduler.Default);

        // 这里传递 _cameraPool 给协调器（如果协调器是独立引用的，可能需要 Register 逻辑，
        // 但根据您的代码，协调器似乎是依赖外部注册或共享引用的。
        // *注意*：如果 Coordinator 需要显式注册，请在这里补上：
        foreach (var dev in _cameraPool.Values) _coordinator.Register(dev);

        _sysLog.Warning($"[Core] 设备管理引擎启动成功, 当前管理 {_cameraPool.Count} 路设备");
        await Task.CompletedTask;
    }

    /// <summary>
    /// 获取当前所有相机的全局状态简报
    /// </summary>
    public IEnumerable<(long Id, string Ip, VideoSourceStatus Status)> GetGlobalStatus()
    {
        return _cameraPool.Values.Select(v => (v.Id, v.Config.IpAddress, v.Status));
    }

    #endregion

    #region --- 4. 监控数据采集 (Telemetry Collection) ---

    /// <summary>
    /// 获取全量相机实时遥测数据快照 (MonitorController 使用)
    /// </summary>
    public IEnumerable<CameraTelemetryInfo> GetTelemetrySnapshot()
    {
        return _cameraPool.Values.Select(cam =>
        {
            // 健康度评分算法
            int healthScore = 100;
            if (cam.Status == VideoSourceStatus.Faulted)
                healthScore = 0;
            else if (cam.Status == VideoSourceStatus.Reconnecting)
                healthScore = 60;
            else if (cam.RealFps < 1.0 && cam.Status == VideoSourceStatus.Playing)
                healthScore = 40;

            return new CameraTelemetryInfo
            {
                DeviceId = cam.Id,
                Name = cam.Config.Name,
                IpAddress = cam.Config.IpAddress,
                Status = cam.Status.ToString(),
                IsOnline = cam.IsPhysicalOnline,
                Fps = cam.RealFps,
                Bitrate = cam.RealBitrate,
                TotalFrames = cam.TotalFrames,
                HealthScore = healthScore,
                LastErrorMessage = cam.Status == VideoSourceStatus.Faulted ? "设备故障或网络中断" : null,
                Timestamp = DateTime.Now,
                Width = cam.Width,
                Height = cam.Height,
            };
        }).ToList();
    }

    #endregion

    #region --- 5. 配置热更新 (Config Hot Update) ---

    /// <summary>
    /// 智能更新设备配置 (含冷热分离逻辑)
    /// </summary>
    public async Task UpdateDeviceConfigAsync(long deviceId, DeviceUpdateDto dto)
    {
        if (!_cameraPool.TryGetValue(deviceId, out var device))
        {
            _sysLog.Warning($"[Core] 设备更新制作, ID:{deviceId} 不存在.");
            throw new KeyNotFoundException($"设备 ID:{deviceId} 不存在.");
        }

        // 1. 审计
        _sysLog.Debug($"[Core] 响应设备配置更新请求, ID:{deviceId}.");

        // ============================================================
        // 【核心修复：手动解除冷冻】
        // [原因] 用户已干预配置，无论之前是否认证失败，都应立即重置标记
        // 这样下一次 Coordinator (5秒内) 扫描时，会因为 IsAuthFailed == false 
        // 且经过了 NormalRetryMs (30s) 而立即尝试拉起。
        // ============================================================
        device.ResetResilience();

        // 2. 创建副本进行对比
        var oldConfig = device.Config;
        var newConfig = oldConfig.DeepCopy();

        // 3. 映射 DTO 值
        if (dto.IpAddress != null) newConfig.IpAddress = dto.IpAddress;
        if (dto.Port != null) newConfig.Port = dto.Port.Value;
        if (dto.Username != null) newConfig.Username = dto.Username;
        if (dto.Password != null) newConfig.Password = dto.Password;
        if (dto.ChannelIndex != null) newConfig.ChannelIndex = dto.ChannelIndex.Value;
        if (dto.StreamType != null) newConfig.StreamType = dto.StreamType.Value;
        if (dto.Name != null) newConfig.Name = dto.Name;
        if (dto.Brand != null) newConfig.Brand = (DeviceBrand)dto.Brand;

        newConfig.RtspPath = dto.RtspPath;
        newConfig.MainboardIp = dto.MainboardIp;
        newConfig.MainboardPort = dto.MainboardPort;
        newConfig.RenderHandle = dto.RenderHandle;

        // 4. 判定冷热更新
        bool needColdRestart =
            newConfig.IpAddress != oldConfig.IpAddress ||
            newConfig.Port != oldConfig.Port ||
            newConfig.Username != oldConfig.Username ||
            newConfig.Password != oldConfig.Password ||
            newConfig.ChannelIndex != oldConfig.ChannelIndex ||
            newConfig.RtspPath != oldConfig.RtspPath ||
            newConfig.RenderHandle != oldConfig.RenderHandle ||
            newConfig.Brand != oldConfig.Brand;

        if (needColdRestart)
        {
            _sysLog.Debug($"[Core] 检测到核心参数变更, 执行冷重启, ID:{deviceId}.");
            bool wasRunning = device.IsRunning;

            // A. 彻底停止
            if (device.IsActived) await device.StopAsync();

            // B. 写入新配置
            device.UpdateConfig(newConfig);

            // C. 自动重启
            if (wasRunning)
                await device.StartAsync();
        }
        else
        {
            _sysLog.Debug($"[Core] 检测到运行时参数变更, 执行热更新, ID:{deviceId}.");

            // A. 更新配置数据
            device.UpdateConfig(newConfig);

            // B. 在线应用策略
            if (device.IsActived)
            {
                // Optimized: 仅构造真正发生变化的参数
                var options = new DynamicStreamOptions();

                // 判定码流是否真的变了（或者 DTO 明确传了新值）
                if (dto.StreamType.HasValue && dto.StreamType != oldConfig.StreamType)
                {
                    options.StreamType = dto.StreamType.Value;
                }

                // 判定句柄是否真的变了
                // Modified: 只有当 DTO 的句柄与旧配置不一致时，才放入 options
                if (dto.RenderHandle != oldConfig.RenderHandle)
                {
                    options.RenderHandle = (IntPtr)dto.RenderHandle;
                }

                // 只有当至少有一个参数需要更新时，才调用底层
                // 假设 DynamicStreamOptions 内部有检测是否有值的方法，或者判断其属性
                if (options.StreamType.HasValue || options.RenderHandle.HasValue)
                {
                    device.ApplyOptions(options);
                }
            }
        }
    }

    #endregion

    #region --- 6. 资源清理 (Disposal) ---

    public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult();

    /// <summary>
    /// 释放资源
    /// </summary>
    /// <returns></returns>
    public async ValueTask DisposeAsync()
    {
        if (_isDisposed) return;
        _isDisposed = true;
        _isEngineStarted = false;

        try
        {
            _globalCts.Cancel();

            var devices = _cameraPool.Values.ToArray();
            _cameraPool.Clear();

            var disposeTasks = devices.Select(async device =>
            {
                try { await device.DisposeAsync(); }
                catch { }
            });
            await Task.WhenAll(disposeTasks);

            try
            {
                HikSdkManager.Uninitialize();
            }
            catch { }

            try
            {
                DahuaSdkManager.Uninitialize();
            }
            catch { }
        }
        finally
        {
            _globalCts.Dispose();
        }
    }

    #endregion

    #region --- 7. 内部辅助 (Helpers) ---

    /// <summary>
    /// 创建设备实例
    /// </summary>
    /// <param name="config"></param>
    /// <returns></returns>
    private BaseVideoSource CreateDeviceInstance(VideoSourceConfig config)
    {
        return config.Brand switch
        {
            DeviceBrand.HikVision => new HikVideoSource(config),
            DeviceBrand.Dahua => new DahuaVideoSource(config),

            // 使用模式匹配获取不匹配的值，记录详细的 DTO 上下文
            _ => HandleUnsupportedBrand(config)
        };
    }

    /// <summary>
    /// 处理不支持的设备品牌
    /// </summary>
    /// <param name="config"></param>
    /// <returns></returns>
    /// <exception cref="NotSupportedException"></exception>
    private BaseVideoSource HandleUnsupportedBrand(VideoSourceConfig config)
    {
        // 1. 构造错误消息
        string errorMsg = $"❌ 不支持的设备品牌: {config.Brand} (ID: {config.Id}, Name: {config.Name})";

        // 2. 写入日志 - 建议带上 config 的解构信息，方便排查是否是前端传参错误
        _sysLog.Error($"[Core] {errorMsg} | 配置详情: " + "{@Config}", config);

        // 3. 抛出异常，阻止程序进入不确定状态
        throw new NotSupportedException(errorMsg);
    }

    #endregion

    /// <summary>
    /// [新增] 获取当前管理的所有相机设备（兼容网络引擎接口）
    /// </summary>
    public IEnumerable<BaseVideoSource> GetAllCameras()
    {
        // 复用现有的 GetAllDevices 逻辑
        return GetAllDevices();
    }

    #region --- [新增] 状态事件总线 (SDK 对外接口) ---

    /// <summary>
    /// 当设备在线/离线状态发生变更时触发
    /// <para>参数1: DeviceId</para>
    /// <para>参数2: IsOnline (true=在线, false=离线)</para>
    /// <para>参数3: Reason (变更原因)</para>
    /// </summary>
    public event Action<long, string, bool, string>? OnDeviceStatusChanged;

    /// <summary>
    /// [内部方法] 供 Sentinel 调用，触发事件冒泡
    /// </summary>
    internal void NotifyStatusChange(long deviceId, string ipAddress, bool isOnline, string reason)
    {
        // 仅仅是触发 C# 事件，完全不知道网络发送的存在
        OnDeviceStatusChanged?.Invoke(deviceId, ipAddress, isOnline, reason);
    }

    #endregion
}