using Ayay.SerilogLogs;
using Grpc.Net.Client;
using Serilog;
using SHH.CameraSdk;
using SHH.Contracts.Grpc;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;

namespace SHH.CameraService;

public static class Bootstrapper
{
    #region ParseConfigAndInitLogger

    /// <summary>
    /// 解析参数并初始化 Serilog
    /// </summary>
    public static (ServiceConfig Config, LogOptions opts, bool IsDebug) ParseConfigAndInitLogger(string[] args)
    {
        bool isDebugArgs = args.Length == 0;

        // =================================================================================
        // 1. 模拟调试参数
        // 原理：直接构造 string[]，完全模拟 OS 传递给 Main 的参数结构，避免 Split 带来的空格解析风险
        // =================================================================================
        if (isDebugArgs)
        {
            args = new[]
            {
                "--appid",   "CameraApp_01",
                
                // 视频流地址 (格式: IP,Port,Type,Desc)
                "--uris",    "localhost,9001,调试PC;",
                "--uris",    "localhost,9002,调试PC;",

                // 日志中心配置 (格式: IP,Port,Desc)
                "--sequris", "58.216.225.5,20026,日志处置中心;",
                "--seqkey",  "Shine978697953780;",
                //"--seqkey",  "Shine899195994250;",

                // 端口策略
                "--mode",    "1",
                "--ports",   "5000,100"
            };
        }

        var config = ServiceConfig.BuildFromArgs(args);

        string pcCode = config.SeqApiKey.Replace("Shine", "");
        var ops = new LogOptions
        {
            AppId = config.AppId,
            LogRootPath = $@"D:\Logs\{config.AppId}",

            // ★这里改为从 config 读取，如果没配则留空或给个默认值
            SeqServerUrl = config.SeqServerUrl,
            SeqApiKey = config.SeqApiKey,
            PcCode = Regex.Replace(pcCode, ".{3}", "$0-").TrimEnd('-'),

            MaxRetentionDays = 10,
            FileSizeLimitBytes = 1024L * 1024 * 1024,
            RollOnFileSizeLimit = true,
        };
        LogBootstrapper.Init(ops);

        return (config, ops, isDebugArgs);
    }

    #endregion

    #region Shutdown

    /// <summary>
    /// 统一的资源释放与关闭方法
    /// </summary>
    /// <summary>
    /// 统一的资源释放与关闭方法
    /// </summary>
    /// <param name="reason">退出原因</param>
    /// <param name="exitCode">退出码 (0=正常, 非0=异常)</param>
    public static void Shutdown(string reason, int exitCode = 0)
    {
        // 创建一个临时的上下文 Logger，防止全局 Logger 已被部分释放
        var sysLog = Log.ForContext("SourceContext", LogModules.Core);

        try
        {
            // =========================================================
            // 1. 写日志
            // =========================================================
            if (exitCode != 0)
            {
                sysLog.Fatal($"[Core] 💀 [程序终止] {reason} (Code: {exitCode})");
            }
            else
            {
                sysLog.Information($"[Core] 👋 [程序退出] {reason}");
            }

            // =========================================================
            // 2. 核心硬件资源释放 (关键！)
            // =========================================================
            // 防止 SDK 句柄残留导致下次启动无法连接相机
            try
            {
                sysLog.Information("[Core] 正在清理 Hikvision SDK 资源...");

                // 如果你的项目中引用了 SDK，请务必解开这行注释
                HikNativeMethods.NET_DVR_Cleanup();
            }
            catch (Exception ex)
            {
                sysLog.Error(ex, "⚠️ SDK 资源清理失败");
            }

            // =========================================================
            // 3. 日志强制落盘
            // =========================================================
            // Environment.Exit 是暴力退出，finally 块不会执行，
            // 必须手动 Flush 确保最后一条日志写入磁盘。
            Log.CloseAndFlush();
        }
        catch
        {
            // 忽略所有清理过程中的错误，确保一定要执行到 Environment.Exit
        }

        // =========================================================
        // 4. 开发环境交互 (生产环境自动跳过)
        // =========================================================
        // 只有在调试器挂载时才暂停，防止 Docker/Service 环境卡死
        if (System.Diagnostics.Debugger.IsAttached)
        {
            Console.ForegroundColor = exitCode == 0 ? ConsoleColor.Green : ConsoleColor.Red;
            Console.WriteLine($"\n[Debug模式] 按下任意键退出... (ExitCode: {exitCode})");
            Console.ResetColor();
            Console.ReadKey();
        }

        // =========================================================
        // 5. 暴力且彻底地结束进程
        // =========================================================
        Environment.Exit(exitCode);
    }

    #endregion

    #region ScanForAvailablePort

    /// <summary>
    /// 扫描可用端口
    /// </summary>
    public static int ScanForAvailablePort(ServiceConfig config, ILogger logger)
    {
        logger.Information($"[Core] 🔍 开始端口检测: 起始={config.BasePort}, 范围={config.MaxPortRange}");

        for (int i = 0; i <= config.MaxPortRange; i++)
        {
            int currentPort = config.BasePort + i;
            if (CheckPortAvailable(currentPort))
            {
                if (currentPort != config.BasePort)
                {
                    logger.Warning($"[Core] ⚙️ 端口自动漂移: {config.BasePort} -> {currentPort}");
                }
                else
                {
                    logger.Information($"[Core] ✅ 端口检测通过: {currentPort}");
                }
                return currentPort;
            }
            logger.Debug($"[Core] ⚠️ 端口 {currentPort} 被占用，尝试下一个...");
        }
        return -1;
    }

    #endregion

    #region CheckPortAvailable

    /// <summary>
    /// 检查端口是否可用
    /// </summary>
    /// <param name="port"></param>
    /// <returns></returns>
    private static bool CheckPortAvailable(int port)
    {
        try
        {
            using var listener = new TcpListener(IPAddress.Any, port);
            listener.Start();
            listener.Stop();
            return true;
        }
        catch
        {
            return false;
        }
    }

    #endregion

    #region WarmUpHardware

    /// <summary>
    /// 硬件预热
    /// </summary>
    public static void WarmUpHardware(ILogger logger)
    {
        logger.Information("[Core] Hik、Dahua Sdk 开始预热.");
        try
        {
            HikNativeMethods.NET_DVR_Init();
            HikSdkManager.ForceWarmUp();
            logger.Information("[Core] 💡Hik Sdk 预热成功.");
        }
        catch (Exception ex)
        {
            logger.Error(ex, "[Core] ⚠️ Hik Sdk 预热失败.");
        }

        try
        {
            DahuaSdkManager.ForceWarmUp();
            logger.Information("[Core] Dahua Sdk 预热成功.");
        }
        catch (Exception ex)
        {
            logger.Error(ex, "[Core] ⚠️ Dahua Sdk 预热失败.");
        }
    }

    #endregion

    #region RegisterToGatewayAsync

    /// <summary>
    /// 向网关注册实例
    /// </summary>
    public static async Task RegisterToGatewayAsync(ServiceConfig config)
    {
        if (!config.CommandEndpoints.Any()) return;
        var gRpcLog = Log.ForContext("SourceContext", LogModules.gRpc);

        // // Optimized: 并发任务集合，实现多目标同时注册
        var registrationTasks = config.CommandEndpoints.Select(async endpoint =>
        {
            string targetUrl = endpoint.Uri.Replace("tcp://", "http://");

            // // Modified: 将 try-catch 移入内部，确保单个端点失败不影响其他端点
            try
            {
                using var channel = GrpcChannel.ForAddress(targetUrl);
                var client = new GatewayProvider.GatewayProviderClient(channel);

                gRpcLog.Information($"[gRpc] 正在执行预注册: {targetUrl}");

                var resp = await client.RegisterInstanceAsync(new RegisterRequest
                {
                    InstanceId = config.AppId,
                    Version = "2.0.0-grpc",
                    ServerIp = "127.0.0.1",
                    WebapiPort = config.BasePort,
                    StartTimeTicks = DateTime.Now.Ticks,
                    ProcessId = Environment.ProcessId,
                    Description = endpoint.Description // 携带备注信息
                });

                gRpcLog.Information($"[gRpc] 💡预注册成功: {targetUrl} -> {resp.Message}");
            }
            catch (Exception ex)
            {
                // // Optimized: 记录具体哪个端点失败，但不阻断流程
                gRpcLog.Error($"[gRpc] ⚠️ 预注册尝试失败 ({targetUrl}): {ex.Message}");
            }
        });

        // 等待所有注册任务完成
        await Task.WhenAll(registrationTasks);

        //try
        //{
        //    var cfgEndpoints = config.CommandEndpoints;
        //    for(var i=0; i<cfgEndpoints.Count; i++)
        //    {
        //        // 将 tcp:// 转换为 http:// 以适配 gRpc
        //        string targetUrl = cfgEndpoints[i].Uri.Replace("tcp://", "http://");

        //        using var channel = GrpcChannel.ForAddress(targetUrl);
        //        var client = new GatewayProvider.GatewayProviderClient(channel);

        //        gRpcLog.Information($"[gRpc] 正在执行预注册: {targetUrl}");
        //        var resp = await client.RegisterInstanceAsync(new RegisterRequest
        //        {
        //            InstanceId = config.AppId,
        //            Version = "2.0.0-grpc",
        //            ServerIp = "127.0.0.1",
        //            WebapiPort = config.BasePort, // 使用扫描后的新端口
        //            StartTimeTicks = DateTime.Now.Ticks,
        //            ProcessId = Environment.ProcessId,
        //            Description = ""
        //        });
        //        gRpcLog.Information($"[gRpc] 💡预注册成功: {resp.Message}");
        //    }
        //}
        //catch (Exception ex)
        //{
        //    gRpcLog.Error($"[gRpc] ⚠️ 预注册尝试失败: {ex.Message}");
        //}
    }

    #endregion
}