using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using PCL.Core.App;
using PCL.Core.Logging;
using PCL.Core.Utils.OS;

namespace PCL.Core.IO;

public static class PipeComm
{
    private static void _PipeLog(string message) => LogWrapper.Trace("Pipe", message);
    private static void _PipeLogDebug(string message) => LogWrapper.Debug("Pipe", message);

    /// <summary>
    /// 用于命名管道通信的统一字符编码
    /// </summary>
    public static readonly Encoding PipeEncoding = Encoding.UTF8;

    /// <summary>
    /// 用于命名管道通信的统一终止符
    /// </summary>
    public const char PipeEndingChar = (char)27; // '\e' (ESC)

    /// <summary>
    /// 在新的工作线程启动命名管道服务端
    /// </summary>
    /// <param name="identifier">服务端标识，用于日志标识及工作线程的命名</param>
    /// <param name="pipeName">命名管道名称</param>
    /// <param name="loopCallback">客户端连接后的回调函数，将会提供用于读取和写入数据的流，以及客户端进程 ID，返回 <c>true</c> 表示继续等待下一个客户端连接，返回 <c>false</c> 则停止服务端运行</param>
    /// <param name="stopCallback">服务端停止后的回调函数</param>
    /// <param name="stopWhenException">指定当回调函数抛出异常时是否停止服务端运行，使用 <c>true</c> 表示停止</param>
    /// <param name="allowedProcessId">允许连接的客户端进程 ID，如为 Nothing 则允许所有</param>
    public static NamedPipeServerStream StartPipeServer(string identifier, string pipeName, Func<StreamReader, StreamWriter, Process?, bool> loopCallback, Action? stopCallback = null, bool stopWhenException = false, int[]? allowedProcessId = null)
    {
        var pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1024, 1024);
        var threadName = $"PipeServer/{identifier}";

        Basics.RunInNewThread(() =>
        {
            LogWrapper.Debug("Pipe", $"{identifier}: {pipeName} 服务端已在 '{threadName}' 工作线程启动");
            var hasNextLoop = true;
            var connected = false;

            while (hasNextLoop)
            {
                try
                {
                    hasNextLoop = false;
                    pipe.WaitForConnection(); // 等待客户端连接
                    // 获取客户端进程实例并校验
                    Process? clientProcess = null;
                    var clientProcessId = 0;
                    try
                    {
                        var pid = KernelInterop.GetNamedPipeClientProcessId(pipe.SafePipeHandle.DangerousGetHandle());
                        clientProcessId = (int)pid;
                        if (allowedProcessId is not null)
                        {
                            var denied = allowedProcessId.All(id => id != clientProcessId);
                            if (denied)
                            {
                                hasNextLoop = true;
                                pipe.Disconnect();
                                _PipeLog($"[Pipe] {identifier}: 已拒绝 {clientProcessId}");
                                continue;
                            }
                        }
                        clientProcess = Process.GetProcessById(clientProcessId);
                    }
                    catch (Exception)
                    {
                        if (allowedProcessId is not null)
                        {
                            hasNextLoop = true;
                            throw;
                        }
                    }
                    connected = true;
                    LogWrapper.Debug("Pipe", $"{identifier}: {clientProcessId} 已连接");
                    // 初始化读取/写入流
                    var reader = new StreamReader(pipe, PipeEncoding, false, 1024, true);
                    var writer = new StreamWriter(pipe, PipeEncoding, 1024, true);
                    // 执行回调函数
                    hasNextLoop = loopCallback(reader, writer, clientProcess);
                    // 写入终止符
                    writer.Write(PipeEndingChar);
                    writer.Flush(); // 刷新写入缓冲
                    reader.Read(); // 等待客户端
                }
                catch (Exception ex)
                {
                    if (!pipe.IsConnected && connected && ex is IOException)
                    {
                        _PipeLogDebug($"{identifier}: 客户端连接已丢失");
                        hasNextLoop = true;
                    }
                    else
                    {
                        LogWrapper.Warn(ex, "Pipe",  $"{identifier}: 服务端出错");
                        if (stopWhenException) hasNextLoop = false;
                    }
                }
                try
                {
                    pipe.Disconnect();
                }
                catch (InvalidOperationException)
                {
                    // 由于没妈的巨硬给的 IsConnected 不一定是准确的，需要运行 Disconnect() 确保管道断开连接
                    // 如果已经断开会抛出 InvalidOperationException 这里直接忽略掉
                }
                connected = false;
                _PipeLogDebug($"{identifier}: 已断开连接");
            }

            // 释放资源并执行停止回调
            pipe.Dispose();
            _PipeLogDebug($"{identifier}: 服务端已停止");
            stopCallback?.Invoke();
        }, threadName);

        return pipe;
    }
}
