using Ayay.SerilogLogs;
using Serilog;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Text.RegularExpressions;

namespace SHH.MjpegPlayer
{
    /// <summary>
    /// 网口占用检测
    /// </summary>
    public static class NetPortExtension
    {
        private static ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);

        #region IsServerPort

        /// <summary>
        /// 是否端口
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsServerPort(this int value)
        {
            if (value > 0 && value < 65535)
                return true;

            return false;
        }

        #endregion

        #region IsPortOccupied

        /// <summary>
        /// 端口占用检测
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static bool IsPortOccupied(this int port)
        {
            var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
            IPEndPoint[] activeListeners = ipProperties.GetActiveTcpListeners();
            foreach (var endPoint in activeListeners)
            {
                if (endPoint.Port == port)
                    return true; // 端口被占用
            }

            return false; // 端口可用
        }

        #endregion

        #region GetProcessIdByPort

        /// <summary>
        /// 查询端口占用进程 Pid
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static int GetProcessIdByPort(this int port)
        {
            try
            {
                using (Process proc = new Process())
                {
                    proc.StartInfo.FileName = "cmd.exe";
                    proc.StartInfo.Arguments = $"/c netstat -ano | findstr :{port}";
                    proc.StartInfo.UseShellExecute = false;
                    proc.StartInfo.RedirectStandardOutput = true;
                    proc.StartInfo.CreateNoWindow = true;

                    proc.Start();
                    string output = proc.StandardOutput.ReadToEnd();
                    proc.WaitForExit();

                    // 解析输出（示例：TCP    0.0.0.0:8080    0.0.0.0:0    LISTENING    1234）
                    Match match = Regex.Match(output, @":\d+\s+.*?LISTENING\s+(\d+)");
                    if (match.Success && int.TryParse(match.Groups[1].Value, out int pid))
                        return pid;
                }

                return 0;
            }
            catch (Exception ex)
            {
                _sysLog.Warning("查询端口占用进程出错", ex.Message, ex.StackTrace);
                return 0;
            }
        }

        #endregion

        #region GetProcessIdByPort

        /// <summary>
        /// 查询端口占用进程 Pid
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static string GetProcessNameIdByPort(this int port)
        {
            try
            {
                using (Process proc = new Process())
                {
                    proc.StartInfo.FileName = "cmd.exe";
                    proc.StartInfo.Arguments = $"/c netstat -ano | findstr :{port}";
                    proc.StartInfo.UseShellExecute = false;
                    proc.StartInfo.RedirectStandardOutput = true;
                    proc.StartInfo.CreateNoWindow = true;

                    proc.Start();
                    string output = proc.StandardOutput.ReadToEnd();
                    proc.WaitForExit();

                    // 解析输出（示例：TCP    0.0.0.0:8080    0.0.0.0:0    LISTENING    1234）
                    Match match = Regex.Match(output, @":\d+\s+.*?LISTENING\s+(\d+)");
                    if (match.Success && int.TryParse(match.Groups[1].Value, out int pid))
                    {
                        using (Process process = Process.GetProcessById(pid))
                            return process.ProcessName;
                    }
                }

                return string.Empty;
            }
            catch (Exception ex)
            {
                _sysLog.Warning($"查询端口占用进程出错, 错误信息:{ex.Message} {ex.StackTrace}");
                return string.Empty;
            }
        }

        #endregion

        #region PortOccupiedProc

        /// <summary>
        /// 端口占用检测并杀掉进程
        /// </summary>
        /// <param name="port"></param>
        /// <returns>返回占用端口清理结果</returns>
        public static bool PortOccupiedProc(this int port)
        {
            if (port.IsPortOccupied())
            {
                _sysLog.Warning("服务器端口被占用, Port: {port}");

                // 等待 5 秒
                for (var i = 0; i < 10; i++)
                    Thread.Sleep(500);

                // 查找占用端口的进程
                var pid = port.GetProcessIdByPort();
                if (pid != 0)
                {
                    // 获取进程名
                    string procName = pid.GetProcessName();

                    // 找到占用端口的进程
                    _sysLog.Warning($"找到占用端口进程 Pid: {pid} 进程名:{procName}, 5 秒后即将尝试杀掉占用端口的进程.");

                    // 等待 5 秒
                    for (var i = 0; i < 10; i++)
                        Thread.Sleep(500);

                    // 杀掉指定进程
                    if (!pid.KillProcessByPid(procName))
                    {
                        // 退出应用
                        return false;
                    }

                    // 等待 2 秒
                    Thread.Sleep(2000);
                    return true;
                }

                return false;
            }

            return true;
        }

        #endregion
    }
}