using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SHH.ProcessLaunchers
{
    /// <summary>
    /// 进程管理器 (核心实现类)
    /// <para>核心职责：作为对外统一入口 (Facade)，维护所有受管进程的容器。</para>
    /// <para>主要功能：负责路由外部指令(启动/停止)到具体的进程实例，并处理事件分发。</para>
    /// </summary>
    public class ProcessManager : IProcessManager, IDisposable
    {
        #region --- 1. 字段与事件 (Fields & Events) ---

        /// <summary>
        /// 线程安全的进程容器
        /// <para>Key: ProcessConfig.Id (唯一标识)</para>
        /// <para>Value: ManagedProcess (受管实例)</para>
        /// </summary>
        private readonly ConcurrentDictionary<string, ManagedProcess> _processes
            = new ConcurrentDictionary<string, ManagedProcess>();

        /// <summary>
        /// 日志服务接口 (依赖注入)
        /// </summary>
        private readonly ILauncherLogger _logger;

        // ---------------------------------------------------------
        // 对外暴露的事件定义
        // ---------------------------------------------------------

        /// <summary>
        /// 对外事件：当接收到任意子进程的标准输出/错误流时触发
        /// </summary>
        public event EventHandler<ProcessOutputEventArgs> OnOutputReceived;

        /// <summary>
        /// 对外事件：当任意子进程的状态发生变更时触发
        /// </summary>
        public event EventHandler<ProcessStateEventArgs> OnStateChanged;

        #endregion

        #region --- 2. 构造与析构 (Constructor & Dispose) ---

        /// <summary>
        /// 初始化进程管理器实例
        /// </summary>
        /// <param name="logger">日志实现类 (若外部未传入，则内部自动使用 NullLogger 以防止空引用异常)</param>
        public ProcessManager(ILauncherLogger logger = null)
        {
            // 规范化：使用空合并运算符确保 _logger 永不为 null
            _logger = logger ?? new NullLogger();
        }

        /// <summary>
        /// 销毁资源，停止所有进程并清理事件订阅
        /// </summary>
        public void Dispose()
        {
            // 1. 停止所有子进程 (触发 Kill 操作，清理进程树)
            StopAll();

            // 2. 清空内部容器引用
            _processes.Clear();

            // 3. 移除所有外部事件订阅，防止 UI 端因未解绑而导致的内存泄露
            OnOutputReceived = null;
            OnStateChanged = null;
        }

        #endregion

        #region --- 3. 公共 API 实现 (Public Methods) ---

        /// <summary>
        /// 注册一个新的进程配置到管理器中
        /// </summary>
        /// <param name="config">进程配置对象 (包含 Exe路径、参数、熔断策略等)</param>
        /// <exception cref="ArgumentException">当 Id 为空时抛出</exception>
        /// <exception cref="InvalidOperationException">当 Id 已存在时抛出</exception>
        public void Register(ProcessConfig config)
        {
            // 1. 基础参数校验：确保 Id 存在
            if (string.IsNullOrWhiteSpace(config.Id))
                throw new ArgumentException("进程配置无效：必须包含唯一的 Id");

            // 2. 防重复注册校验：确保字典中没有相同的 Key
            if (_processes.ContainsKey(config.Id))
                throw new InvalidOperationException($"进程 Id '{config.Id}' 已存在，禁止重复注册。");

            // 3. 实例化受管进程对象 (传入 this 指针是为了后续回调 DispatchXXX 方法)
            var process = new ManagedProcess(config, this, _logger);

            // 4. 加入线程安全字典
            if (_processes.TryAdd(config.Id, process))
            {
                _logger.LogLifecycle(config.Id, LogAction.Output, LogTrigger.System,
                    $"进程配置已注册: {config.DisplayName}");
            }
        }

        /// <summary>
        /// 启动指定 ID 的进程
        /// </summary>
        /// <param name="id">进程的唯一标识符 (ProcessConfig.Id)</param>
        public void Start(string id)
        {
            // 尝试获取指定 ID 的进程实例
            if (_processes.TryGetValue(id, out var p))
            {
                // 调用内部实例的启动逻辑，操作归因标记为"User" (用户手动)
                p.ExecuteStart(LogTrigger.User, "用户手动启动指令");
            }
            else
            {
                // 如果找不到，记录错误日志
                _logger.LogLifecycle(id, LogAction.Error, LogTrigger.User, "启动失败：未找到指定 ID 的进程配置");
            }
        }

        /// <summary>
        /// [异步] 有序批量启动所有进程
        /// <para>按照 StartupOrder 从小到大排序启动，并支持启动间隙延时 (PostStartupDelayMs)。</para>
        /// </summary>
        /// <returns>异步任务</returns>
        public async Task StartAllAsync()
        {
            _logger.LogLifecycle("ALL", LogAction.Start, LogTrigger.User, "执行有序批量启动");

            // 1. 数据准备：从字典取出所有进程，并按配置进行排序
            //    排序规则：StartupOrder (小->大) -> Id (字母序) 以保证启动顺序的确定性
            var sortedList = _processes.Values
                .OrderBy(p => p.Config.StartupOrder) // 按用户指定的权重排
                .ThenBy(p => p.Config.Id)            // 权重一样时按 ID 排
                .ToList();

            // 2. 顺序执行启动循环
            foreach (var p in sortedList)
            {
                // 同步调用启动指令（注意：这里不等待进程完全 Ready，只负责拉起进程）
                p.ExecuteStart(LogTrigger.User, "有序批量启动");

                // 3. 处理启动间隙延迟 (错峰启动)
                //    作用：防止多个重型进程同时启动导致 CPU/IO 瞬间拥堵
                int delay = p.Config.PostStartupDelayMs;
                if (delay > 0)
                {
                    // 异步等待指定毫秒数，释放线程控制权
                    await Task.Delay(delay);
                }
            }

            _logger.LogLifecycle("ALL", LogAction.Start, LogTrigger.User, "有序批量启动完成");
        }

        /// <summary>
        /// 停止指定 ID 的进程
        /// </summary>
        /// <param name="id">进程的唯一标识符</param>
        public void Stop(string id)
        {
            if (_processes.TryGetValue(id, out var p))
            {
                p.ExecuteStop(LogTrigger.User, "用户手动停止指令");
            }
        }

        /// <summary>
        /// 批量停止所有进程 (并发执行)
        /// </summary>
        public void StopAll()
        {
            _logger.LogLifecycle("ALL", LogAction.Stop, LogTrigger.User, "执行批量停止");

            // 遍历所有进程，使用 Task.Run 并发执行停止，提高效率，无需等待
            foreach (var p in _processes.Values)
            {
                Task.Run(() => p.ExecuteStop(LogTrigger.User, "批量停止"));
            }
        }

        /// <summary>
        /// 重置/复位指定进程的资源报警状态
        /// <para>当用户在 UI 上点击"已处置"后调用此方法，解除报警锁定。</para>
        /// </summary>
        /// <param name="id">进程的唯一标识符</param>
        public void ResetGuard(string id)
        {
            if (_processes.TryGetValue(id, out var p))
            {
                // 调用内部复位逻辑，清除报警锁定状态
                p.ResetGuards();
                _logger.LogLifecycle(id, LogAction.ResourceCheck, LogTrigger.User, "用户手动复位资源报警锁");
            }
        }

        /// <summary>
        /// 获取当前所有进程的实时状态快照
        /// <para>用于 UI 列表的数据绑定或定时刷新。</para>
        /// </summary>
        /// <returns>进程信息快照列表</returns>
        public List<ProcessInfoSnapshot> GetSnapshot()
        {
            // 将字典中的所有受管对象转为 DTO 快照列表
            return _processes.Values.Select(p => p.GetSnapshot()).ToList();
        }

        #endregion

        #region --- 4. 内部事件分发 (Internal Dispatchers) ---

        // 说明：C# 的 event 只能在定义类内部 Invoke。
        // 为了让内部类 ManagedProcess 也能触发 Manager 的对外事件，我们提供了这几个 internal 方法。
        // 这些方法充当了内部类与外部事件之间的桥梁。

        /// <summary>
        /// 分发状态变更事件 (供 ManagedProcess 内部调用)
        /// </summary>
        /// <param name="processId">进程 ID</param>
        /// <param name="newState">新的状态</param>
        internal void DispatchStateChange(string processId, ProcessStatus newState)
        {
            // 线程安全地触发事件
            OnStateChanged?.Invoke(this, new ProcessStateEventArgs
            {
                ProcessId = processId,
                State = newState
            });
        }

        /// <summary>
        /// 分发日志输出事件 (供 ManagedProcess 内部调用)
        /// </summary>
        /// <param name="processId">进程 ID</param>
        /// <param name="content">日志内容</param>
        /// <param name="isError">是否为错误流</param>
        internal void DispatchOutput(string processId, string content, bool isError)
        {
            // 线程安全地触发事件
            OnOutputReceived?.Invoke(this, new ProcessOutputEventArgs
            {
                ProcessId = processId,
                Content = content,
                IsError = isError
            });
        }

        #endregion
    }
}