// Copyright 2012-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0
//
// Adapted from server/service.go and server/service_windows.go in the NATS server Go source.

using System.Runtime.InteropServices;

namespace ZB.MOM.NatsNet.Server.Internal;

/// <summary>
/// Service wrappers for platform-specific server startup behavior.
/// </summary>
public static class ServiceManager
{
    private static readonly Lock ServiceNameLock = new();
    private static string _serviceName = "nats-server";
    private static bool _dockerized;

    /// <summary>
    /// Allows overriding the service name.
    /// Mirrors Go <c>SetServiceName</c>.
    /// </summary>
    public static void SetServiceName(string name)
    {
        if (string.IsNullOrWhiteSpace(name))
            return;

        lock (ServiceNameLock)
        {
            _serviceName = name;
        }
    }

    /// <summary>
    /// Initializes service-related environment flags.
    /// Mirrors Go package <c>init()</c> behavior in service_windows.go.
    /// </summary>
    public static void Init(Func<string, string?>? envLookup = null)
    {
        var lookup = envLookup ?? Environment.GetEnvironmentVariable;
        _dockerized = string.Equals(lookup("NATS_DOCKERIZED"), "1", StringComparison.Ordinal);
    }

    /// <summary>
    /// Runs the server startup action.
    /// Mirrors Go <c>Run</c> behavior from service.go/service_windows.go.
    /// </summary>
    public static Exception? Run(Action startServer, Func<bool>? windowsServiceProbe = null)
    {
        ArgumentNullException.ThrowIfNull(startServer);

        if (_dockerized)
        {
            startServer();
            return null;
        }

        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            startServer();
            return null;
        }

        if (!IsWindowsService(windowsServiceProbe))
        {
            startServer();
            return null;
        }

        return new PlatformNotSupportedException(
            "Windows service hosting is managed by Microsoft.Extensions.Hosting.WindowsServices.");
    }

    /// <summary>
    /// Returns true when running as a Windows service.
    /// Mirrors Go <c>isWindowsService</c>.
    /// </summary>
    public static bool IsWindowsService(Func<bool>? windowsServiceProbe = null)
    {
        if (_dockerized)
            return false;

        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return false;

        if (windowsServiceProbe is not null)
            return windowsServiceProbe();

        return !Environment.UserInteractive;
    }

    /// <summary>
    /// Windows service execution wrapper.
    /// Mirrors Go <c>winServiceWrapper.Execute</c> control loop semantics.
    /// </summary>
    public static (bool serviceSpecificExitCode, uint exitCode) Execute(
        Action startServer,
        Func<TimeSpan, bool> readyForConnections,
        IEnumerable<ServiceControlCommand> changes,
        Action reloadConfig,
        Action shutdown,
        Action reopenLogFile,
        Action enterLameDuckMode,
        Func<string, string?>? envLookup = null,
        Action<string>? logError = null)
    {
        ArgumentNullException.ThrowIfNull(startServer);
        ArgumentNullException.ThrowIfNull(readyForConnections);
        ArgumentNullException.ThrowIfNull(changes);
        ArgumentNullException.ThrowIfNull(reloadConfig);
        ArgumentNullException.ThrowIfNull(shutdown);
        ArgumentNullException.ThrowIfNull(reopenLogFile);
        ArgumentNullException.ThrowIfNull(enterLameDuckMode);

        var startupDelay = TimeSpan.FromSeconds(10);
        var lookup = envLookup ?? Environment.GetEnvironmentVariable;
        var configuredDelay = lookup("NATS_STARTUP_DELAY");
        if (!string.IsNullOrEmpty(configuredDelay))
        {
            if (TimeSpan.TryParse(configuredDelay, out var parsedDelay))
            {
                startupDelay = parsedDelay;
            }
            else
            {
                logError?.Invoke($"Failed to parse \"{configuredDelay}\" as a duration for startup.");
            }
        }

        Task.Run(startServer);

        if (!readyForConnections(startupDelay))
            return (false, 1);

        foreach (var change in changes)
        {
            switch (change)
            {
                case ServiceControlCommand.Interrogate:
                    continue;
                case ServiceControlCommand.Stop:
                case ServiceControlCommand.Shutdown:
                    shutdown();
                    return (false, 0);
                case ServiceControlCommand.ReopenLog:
                    reopenLogFile();
                    break;
                case ServiceControlCommand.LameDuckMode:
                    Task.Run(enterLameDuckMode);
                    break;
                case ServiceControlCommand.ParamChange:
                    reloadConfig();
                    break;
            }
        }

        return (false, 0);
    }

    internal static string CurrentServiceName
    {
        get
        {
            lock (ServiceNameLock)
            {
                return _serviceName;
            }
        }
    }
}

public enum ServiceControlCommand
{
    Interrogate,
    Stop,
    Shutdown,
    ReopenLog,
    LameDuckMode,
    ParamChange
}
