// Copyright 2012-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0

using System.Runtime.InteropServices;
using Shouldly;
using ZB.MOM.NatsNet.Server.Internal;

namespace ZB.MOM.NatsNet.Server.Tests.Internal;

/// <summary>
/// Tests for SignalHandler — mirrors server/signal_test.go.
/// </summary>
public sealed class SignalHandlerTests : IDisposable
{
    public SignalHandlerTests()
    {
        SignalHandler.ResetTestHooks();
        SignalHandler.SetProcessName("nats-server");
    }

    public void Dispose()
    {
        SignalHandler.ResetTestHooks();
        SignalHandler.SetProcessName("nats-server");
    }

    [Fact] // T:3158
    public void CommandToUnixSignal_ShouldMapCorrectly()
    {
        SignalHandler.CommandToUnixSignal(ServerCommand.Stop).ShouldBe(UnixSignal.SigKill);
        SignalHandler.CommandToUnixSignal(ServerCommand.Quit).ShouldBe(UnixSignal.SigInt);
        SignalHandler.CommandToUnixSignal(ServerCommand.Reopen).ShouldBe(UnixSignal.SigUsr1);
        SignalHandler.CommandToUnixSignal(ServerCommand.Reload).ShouldBe(UnixSignal.SigHup);
        SignalHandler.CommandToUnixSignal(ServerCommand.Term).ShouldBe(UnixSignal.SigTerm);
        SignalHandler.CommandToUnixSignal(ServerCommand.LameDuckMode).ShouldBe(UnixSignal.SigUsr2);
    }

    [Fact]
    public void CommandToSignal_ShouldMatchCommandToUnixSignal()
    {
        foreach (var command in Enum.GetValues<ServerCommand>())
        {
            SignalHandler.CommandToSignal(command)
                .ShouldBe(SignalHandler.CommandToUnixSignal(command));
        }
    }

    [Fact] // T:3155
    public void SetProcessName_ShouldNotThrow()
    {
        Should.NotThrow(() => SignalHandler.SetProcessName("test-server"));
    }

    [Fact] // T:3149
    public void IsWindowsService_ShouldReturnFalse()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        SignalHandler.IsWindowsService().ShouldBeFalse();
    }

    [Fact] // T:3148
    public void Run_ShouldInvokeStartAction()
    {
        var called = false;
        SignalHandler.Run(() => called = true);
        called.ShouldBeTrue();
    }

    [Fact] // T:3157
    public void ProcessSignal_InvalidPid_ShouldReturnError()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "not-a-pid");
        err.ShouldNotBeNull();
    }

    [Fact] // T:2919
    public void ProcessSignalInvalidCommand_ShouldSucceed()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        var err = SignalHandler.ProcessSignal((ServerCommand)99, "123");
        err.ShouldNotBeNull();
        err!.Message.ShouldContain("unknown signal");
    }

    [Fact] // T:2920
    public void ProcessSignalInvalidPid_ShouldSucceed()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "abc");
        err.ShouldNotBeNull();
        err!.Message.ShouldBe("invalid pid: abc");
    }

    [Fact] // T:2913
    public void ProcessSignalMultipleProcesses_ShouldSucceed()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        SignalHandler.ResolvePidsHandler = () => [123, 456];

        var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "");
        err.ShouldNotBeNull();
        err!.Message.ShouldBe("multiple nats-server processes running:\n123\n456");
    }

    [Fact] // T:2914
    public void ProcessSignalMultipleProcessesGlob_ShouldSucceed()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        SignalHandler.ResolvePidsHandler = () => [123, 456];
        SignalHandler.SendSignalHandler = static (_, _) => new InvalidOperationException("mock");

        var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "*");
        err.ShouldNotBeNull();
        var lines = err!.Message.Split('\n');
        lines.Length.ShouldBe(3);
        lines[0].ShouldBe(string.Empty);
        lines[1].ShouldStartWith("signal \"stop\" 123:");
        lines[2].ShouldStartWith("signal \"stop\" 456:");
    }

    [Fact] // T:2915
    public void ProcessSignalMultipleProcessesGlobPartial_ShouldSucceed()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        SignalHandler.ResolvePidsHandler = () => [123, 124, 456];
        SignalHandler.SendSignalHandler = static (_, _) => new InvalidOperationException("mock");

        var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "12*");
        err.ShouldNotBeNull();
        var lines = err!.Message.Split('\n');
        lines.Length.ShouldBe(3);
        lines[0].ShouldBe(string.Empty);
        lines[1].ShouldStartWith("signal \"stop\" 123:");
        lines[2].ShouldStartWith("signal \"stop\" 124:");
    }

    [Fact] // T:2916
    public void ProcessSignalPgrepError_ShouldSucceed()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        SignalHandler.ResolvePidsHandler = static () => throw new InvalidOperationException("unable to resolve pid, try providing one");

        var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "");
        err.ShouldNotBeNull();
        err!.Message.ShouldBe("unable to resolve pid, try providing one");
    }

    [Fact] // T:2917
    public void ProcessSignalPgrepMangled_ShouldSucceed()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        SignalHandler.ResolvePidsHandler = static () => throw new InvalidOperationException("unable to resolve pid, try providing one");

        var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "");
        err.ShouldNotBeNull();
        err!.Message.ShouldBe("unable to resolve pid, try providing one");
    }

    [Fact] // T:2918
    public void ProcessSignalResolveSingleProcess_ShouldSucceed()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        var called = false;
        SignalHandler.ResolvePidsHandler = () => [123];
        SignalHandler.SendSignalHandler = (pid, signal) =>
        {
            called = true;
            pid.ShouldBe(123);
            signal.ShouldBe(UnixSignal.SigKill);
            return null;
        };

        var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "");
        err.ShouldBeNull();
        called.ShouldBeTrue();
    }

    [Fact] // T:2921
    public void ProcessSignalQuitProcess_ShouldSucceed()
    {
        ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand.Quit, UnixSignal.SigInt, "123");
    }

    [Fact] // T:2922
    public void ProcessSignalTermProcess_ShouldSucceed()
    {
        ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand.Term, UnixSignal.SigTerm, "123");
    }

    [Fact] // T:2923
    public void ProcessSignalReopenProcess_ShouldSucceed()
    {
        ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand.Reopen, UnixSignal.SigUsr1, "123");
    }

    [Fact] // T:2924
    public void ProcessSignalReloadProcess_ShouldSucceed()
    {
        ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand.Reload, UnixSignal.SigHup, "123");
    }

    [Fact] // T:2925
    public void ProcessSignalLameDuckMode_ShouldSucceed()
    {
        ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand.LameDuckMode, UnixSignal.SigUsr2, "123");
    }

    [Fact] // T:2926
    public void ProcessSignalTermDuringLameDuckMode_ShouldSucceed()
    {
        ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand.Term, UnixSignal.SigTerm, "123");
    }

    [Fact] // T:2927
    public void SignalInterruptHasSuccessfulExit_ShouldSucceed()
    {
        ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand.Quit, UnixSignal.SigInt, "123");
    }

    [Fact] // T:2928
    public void SignalTermHasSuccessfulExit_ShouldSucceed()
    {
        ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand.Term, UnixSignal.SigTerm, "123");
    }

    private static void ProcessSignalCommand_ShouldUseExpectedSignal(ServerCommand command, UnixSignal expectedSignal, string pid)
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return;

        var called = false;
        SignalHandler.SendSignalHandler = (resolvedPid, signal) =>
        {
            called = true;
            resolvedPid.ShouldBe(123);
            signal.ShouldBe(expectedSignal);
            return null;
        };

        var err = SignalHandler.ProcessSignal(command, pid);
        err.ShouldBeNull();
        called.ShouldBeTrue();
    }
}
