using System.Net;
using System.Text;

namespace Metin2Launcher.Tests;

/// <summary>
/// Minimal in-process HTTP server backed by <see cref="HttpListener"/> for
/// telemetry / orchestrator tests. Dependency-free — no TestServer, no
/// WireMock, no new NuGet package.
/// </summary>
public sealed class TestHttpListener : IDisposable
{
    private readonly HttpListener _listener = new();
    private CancellationTokenSource _cts = new();
    private Task? _loop;
    private HttpStatusCode _status = HttpStatusCode.OK;
    private TimeSpan _delay = TimeSpan.Zero;
    private Func<HttpListenerRequest, byte[]>? _bodyFactory;

    public CapturedRequest? LastRequest { get; private set; }

    public string Start()
    {
        int port = GetFreeTcpPort();
        var prefix = $"http://127.0.0.1:{port}/";
        _listener.Prefixes.Add(prefix);
        _listener.Start();
        _loop = Task.Run(LoopAsync);
        return prefix;
    }

    public void RespondWith(HttpStatusCode status) => _status = status;

    public void RespondAfter(TimeSpan delay, HttpStatusCode status)
    {
        _delay = delay;
        _status = status;
    }

    public void RespondBody(Func<HttpListenerRequest, byte[]> factory) => _bodyFactory = factory;

    private async Task LoopAsync()
    {
        while (!_cts.IsCancellationRequested)
        {
            HttpListenerContext ctx;
            try { ctx = await _listener.GetContextAsync().ConfigureAwait(false); }
            catch { return; }

            try
            {
                string body;
                using (var r = new StreamReader(ctx.Request.InputStream, Encoding.UTF8))
                    body = await r.ReadToEndAsync().ConfigureAwait(false);

                LastRequest = new CapturedRequest
                {
                    Method = ctx.Request.HttpMethod,
                    RawUrl = ctx.Request.RawUrl ?? "",
                    Body = body,
                };

                if (_delay > TimeSpan.Zero)
                {
                    try { await Task.Delay(_delay, _cts.Token).ConfigureAwait(false); }
                    catch (OperationCanceledException) { }
                }

                ctx.Response.StatusCode = (int)_status;
                if (_bodyFactory != null)
                {
                    var payload = _bodyFactory(ctx.Request);
                    await ctx.Response.OutputStream.WriteAsync(payload, _cts.Token).ConfigureAwait(false);
                }
                ctx.Response.Close();
            }
            catch
            {
                try { ctx.Response.Abort(); } catch { }
            }
        }
    }

    private static int GetFreeTcpPort()
    {
        var l = new System.Net.Sockets.TcpListener(IPAddress.Loopback, 0);
        l.Start();
        var port = ((System.Net.IPEndPoint)l.LocalEndpoint).Port;
        l.Stop();
        return port;
    }

    public void Dispose()
    {
        try { _cts.Cancel(); } catch { }
        try { _listener.Stop(); _listener.Close(); } catch { }
        try { _loop?.Wait(TimeSpan.FromSeconds(2)); } catch { }
        _cts.Dispose();
    }

    public sealed class CapturedRequest
    {
        public string Method { get; set; } = "";
        public string RawUrl { get; set; } = "";
        public string Body { get; set; } = "";
    }
}
