// Copyright 2012-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Mirrors Go test helpers: RunServer, GetFreePort, etc. from server/test_test.go.

using System.Net;
using System.Net.Sockets;
using Xunit.Abstractions;
using ZB.MOM.NatsNet.Server;

namespace ZB.MOM.NatsNet.Server.IntegrationTests.Helpers;

/// <summary>
/// Server lifecycle helpers for integration tests.
/// Mirrors Go patterns from server/test_test.go: RunServer, GetFreePort, etc.
/// </summary>
internal static class TestServerHelper
{
    /// <summary>
    /// Returns true if a NatsServer can be instantiated with basic options.
    /// Used as a Skip guard — if the server can't boot, all integration tests skip gracefully.
    /// </summary>
    public static bool CanBoot()
    {
        try
        {
            var opts = new ServerOptions
            {
                Host   = "127.0.0.1",
                Port   = -1,
                NoLog  = true,
                NoSigs = true,
            };
            var (server, err) = NatsServer.NewServer(opts);
            if (err != null || server == null)
                return false;

            server.Shutdown();
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Creates and starts a NatsServer with the given options.
    /// Returns the running server and the options used.
    /// Mirrors Go <c>RunServer</c>.
    /// </summary>
    public static (NatsServer Server, ServerOptions Options) RunServer(ServerOptions opts)
    {
        var (server, err) = NatsServer.NewServer(opts);
        if (err != null)
            throw new InvalidOperationException($"Failed to create server: {err.Message}", err);
        if (server == null)
            throw new InvalidOperationException("Failed to create server: NewServer returned null.");

        server.Start();
        return (server, opts);
    }

    /// <summary>
    /// Creates and starts a NatsServer with JetStream enabled and a temp store directory.
    /// Mirrors Go <c>RunServer</c> with JetStream options.
    /// </summary>
    public static NatsServer RunBasicJetStreamServer(ITestOutputHelper? output = null)
    {
        var storeDir = CreateTempDir("js-store-");
        var opts = new ServerOptions
        {
            Host      = "127.0.0.1",
            Port      = -1,
            NoLog     = true,
            NoSigs    = true,
            JetStream = true,
            StoreDir  = storeDir,
        };

        var (server, _) = RunServer(opts);
        return server;
    }

    /// <summary>
    /// Creates and starts a NatsServer using the options parsed from a config file path.
    /// The config file content is read and minimal parsing extracts key options.
    /// Returns the running server and the options.
    /// </summary>
    public static (NatsServer Server, ServerOptions Options) RunServerWithConfig(string configFile)
    {
        var opts = new ServerOptions
        {
            ConfigFile = configFile,
            NoLog      = true,
            NoSigs     = true,
        };

        return RunServer(opts);
    }

    /// <summary>
    /// Finds a free TCP port on loopback.
    /// Mirrors Go <c>GetFreePort</c>.
    /// </summary>
    public static int GetFreePort()
    {
        var listener = new TcpListener(IPAddress.Loopback, 0);
        listener.Start();
        var port = ((IPEndPoint)listener.LocalEndpoint).Port;
        listener.Stop();
        return port;
    }

    /// <summary>
    /// Creates a uniquely named temp directory with the given prefix.
    /// The caller is responsible for deleting it when done.
    /// </summary>
    public static string CreateTempDir(string prefix = "nats-test-")
    {
        var path = Path.Combine(Path.GetTempPath(), prefix + Guid.NewGuid().ToString("N")[..8]);
        Directory.CreateDirectory(path);
        return path;
    }
}
