#region << 版 本 注 释 >>

/*--------------------------------------------------------------------
* 版权所有 (c) 2026 WenJY 保留所有权利。
* CLR版本：4.0.30319.42000
* 机器名称：Mr.Wen's MacBook Pro
* 命名空间：Sln.IntelliBelt.Socket
* 唯一标识：E4DF9972-3038-4900-A2C1-507F30B63F54
*
* 创建者：WenJY
* 电子邮箱：
* 创建时间：2026-05-27 11:13:35
* 版本：V1.0.0
* 描述：
*
*--------------------------------------------------------------------
* 修改人：
* 时间：
* 修改说明：
*
* 版本：V1.0.0
*--------------------------------------------------------------------*/

#endregion << 版 本 注 释 >>

using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Sln.IntelliBelt.Socket;

/// <summary>
/// 端口信息查询结果
/// </summary>
public class PortProcessInfo
{
    public int Port { get; set; }
    public string ProcessName { get; set; }
    public int Pid { get; set; }
    public string LocalAddress { get; set; }
    public string RemoteAddress { get; set; }
    public string State { get; set; }

    public override string ToString()
    {
        return $"端口:{Port} | 程序:{ProcessName} | PID:{Pid} | {LocalAddress}->{RemoteAddress} | 状态:{State}";
    }
}

public static class PortHelper
{
    /// <summary>
    /// 根据端口号获取使用该端口的程序信息
    /// </summary>
    /// <param name="port">端口号</param>
    /// <returns>程序信息列表</returns>
    public static List<PortProcessInfo> GetProcessByPort(int port)
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
            || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            return GetProcessByPortUnix(port);
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            return GetProcessByPortWindows(port);
        }

        throw new PlatformNotSupportedException("不支持当前操作系统");
    }

    #region Unix (macOS / Linux)

    private static List<PortProcessInfo> GetProcessByPortUnix(int port)
    {
        var result = new List<PortProcessInfo>();

        try
        {
            var psi = new ProcessStartInfo
            {
                FileName = "lsof",
                Arguments = $"-i :{port} -n -P",
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            using var process = Process.Start(psi);
            var output = process!.StandardOutput.ReadToEnd();
            process.WaitForExit();

            var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
            // 跳过表头（第一行）
            foreach (var line in lines.Skip(1))
            {
                var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length < 9) continue;

                var command = parts[0];
                var pidStr = parts[1];
                var namePart = parts[8]; // 格式: localhost:port->localhost:port 或 *:port

                if (!int.TryParse(pidStr, out int pid)) continue;

                var info = new PortProcessInfo
                {
                    Port = port,
                    ProcessName = command,
                    Pid = pid
                };

                // 解析地址和状态
                ParseUnixConnection(namePart, info);

                // 如果有状态标识（如 (ESTABLISHED)），取最后一个括号内的内容
                var lastPart = parts[^1];
                if (lastPart.StartsWith('(') && lastPart.EndsWith(')'))
                {
                    info.State = lastPart.Trim('(', ')');
                }
                else
                {
                    info.State = "LISTEN";
                }

                result.Add(info);
            }
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"执行 lsof 失败: {ex.Message}");
        }

        return result;
    }

    private static void ParseUnixConnection(string namePart, PortProcessInfo info)
    {
        // 格式类似: localhost:6000->localhost:51183 或 *:51183
        var arrowIdx = namePart.IndexOf("->");
        if (arrowIdx > 0)
        {
            info.LocalAddress = namePart[..arrowIdx];
            info.RemoteAddress = namePart[(arrowIdx + 2)..];
        }
        else
        {
            info.LocalAddress = namePart;
            info.RemoteAddress = "-";
        }
    }

    #endregion

    #region Windows

    private static List<PortProcessInfo> GetProcessByPortWindows(int port)
    {
        var result = new List<PortProcessInfo>();

        try
        {
            // 1. 用 netstat 找到 PID
            var connections = new List<(string local, string remote, string state, int pid)>();

            var psi = new ProcessStartInfo
            {
                FileName = "netstat",
                Arguments = "-ano",
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            using var process = Process.Start(psi);
            var output = process!.StandardOutput.ReadToEnd();
            process.WaitForExit();

            foreach (var line in output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Skip(4))
            {
                var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length < 5) continue;

                var localAddr = parts[1];  // 如 0.0.0.0:8080
                var remoteAddr = parts[2];
                var state = parts.Length == 5 ? "LISTEN" : parts[3];

                // 取最后一列作为 PID
                var lastPart = parts[^1];
                if (!int.TryParse(lastPart, out int pid)) continue;

                // 检查是否匹配目标端口
                var localPort = localAddr.Split(':').Last();
                var remotePort = remoteAddr.Split(':').Last();
                if (localPort == port.ToString() || remotePort == port.ToString())
                {
                    connections.Add((localAddr, remoteAddr, state, pid));
                }
            }

            // 2. 用 tasklist 找程序名
            var pidNameMap = new Dictionary<int, string>();
            psi = new ProcessStartInfo
            {
                FileName = "tasklist",
                Arguments = "/FO CSV /NH",
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            using var process2 = Process.Start(psi);
            var tasklistOutput = process2!.StandardOutput.ReadToEnd();
            process2.WaitForExit();

            foreach (var line in tasklistOutput.Split('\n', StringSplitOptions.RemoveEmptyEntries))
            {
                var trimmed = line.Trim().Trim('"');
                var csvParts = trimmed.Split("\",\"");
                if (csvParts.Length >= 2 && int.TryParse(csvParts[1], out int pid))
                {
                    pidNameMap[pid] = csvParts[0];
                }
            }

            // 3. 组装结果
            foreach (var (local, remote, state, pid) in connections)
            {
                var processName = pidNameMap.TryGetValue(pid, out var name) ? name : "(未知)";
                result.Add(new PortProcessInfo
                {
                    Port = port,
                    ProcessName = processName,
                    Pid = pid,
                    LocalAddress = local,
                    RemoteAddress = remote,
                    State = state
                });
            }
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"查询失败: {ex.Message}");
        }

        return result;
    }

    #endregion

    /// <summary>
    /// 判断端口是否被本地 Intel 程序（Sln.Intel）占用
    /// </summary>
    /// <param name="port">端口号</param>
    /// <returns>true 表示端口被本地 Sln.Intel 相关进程占用</returns>
    public static bool IsIntelLocalPort(int port)
    {
        var list = GetProcessByPort(port);

        return list.Any(info =>
            // 程序名包含 Sln.Intel（忽略大小写）
            info.ProcessName.Contains("Sln.Intel",
                StringComparison.OrdinalIgnoreCase) //&& IsLocalAddress(info.RemoteAddress);;
        // 且连接双方都在本机（localhost 或 127.0.0.1）
        && IsLocalAddress(info.LocalAddress)
        && IsLocalAddress(info.RemoteAddress));
    }

    /// <summary>
    /// 判断地址是否为本机地址
    /// </summary>
    private static bool IsLocalAddress(string address)
    {
        if (string.IsNullOrEmpty(address)) return false;

        // 支持 localhost、127.0.0.1、*:* 等本机形式
        return address.StartsWith("localhost:", StringComparison.OrdinalIgnoreCase)
            || address.StartsWith("127.0.0.1:", StringComparison.OrdinalIgnoreCase)
            || address.StartsWith("*:", StringComparison.OrdinalIgnoreCase)
            || address.StartsWith("[::1]:", StringComparison.OrdinalIgnoreCase)
            || address.StartsWith("0.0.0.0:", StringComparison.OrdinalIgnoreCase);
    }

    #region 便捷方法

    /// <summary>
    /// 获取端口对应的第一个程序名称（最常用）
    /// </summary>
    /// <param name="port">端口号</param>
    /// <returns>程序名，无结果时返回 null</returns>
    public static string? GetProcessNameByPort(int port)
    {
        var list = GetProcessByPort(port);
        return list.FirstOrDefault()?.ProcessName;
    }

    /// <summary>
    /// 输出端口占用详情到控制台
    /// </summary>
    public static void PrintPortInfo(int port)
    {
        var list = GetProcessByPort(port);
        if (list.Count == 0)
        {
            Console.WriteLine($"端口 {port} 没有被任何程序占用");
            return;
        }
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }
    }

    #endregion
}