using System.Net;
using System.Net.Sockets;
using System.Text;

namespace OF_DL.Tests.Services;

internal sealed class SimpleHttpServer : IDisposable
{
    private readonly TcpListener _listener;
    private readonly Task _handlerTask;
    private readonly byte[] _responseBytes;

    public SimpleHttpServer(string body, DateTime? lastModifiedUtc = null)
    {
        _listener = new TcpListener(IPAddress.Loopback, 0);
        _listener.Start();
        int port = ((IPEndPoint)_listener.LocalEndpoint).Port;
        Url = new Uri($"http://127.0.0.1:{port}/");
        _responseBytes = BuildResponse(body, lastModifiedUtc);
        _handlerTask = Task.Run(HandleOnceAsync);
    }

    public Uri Url { get; }

    public Task Completion => _handlerTask;

    public void Dispose()
    {
        _listener.Stop();
        try
        {
            _handlerTask.Wait(TimeSpan.FromSeconds(1));
        }
        catch
        {
            // ignored
        }
    }

    private async Task HandleOnceAsync()
    {
        using TcpClient client = await _listener.AcceptTcpClientAsync();
        await using NetworkStream stream = client.GetStream();
        await ReadHeadersAsync(stream);
        await stream.WriteAsync(_responseBytes);
    }

    private static async Task ReadHeadersAsync(NetworkStream stream)
    {
        byte[] buffer = new byte[1024];
        int read;
        string data = "";
        while ((read = await stream.ReadAsync(buffer)) > 0)
        {
            data += Encoding.ASCII.GetString(buffer, 0, read);
            if (data.Contains("\r\n\r\n", StringComparison.Ordinal))
            {
                break;
            }
        }
    }

    private static byte[] BuildResponse(string body, DateTime? lastModifiedUtc)
    {
        byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
        StringBuilder header = new();
        header.Append("HTTP/1.1 200 OK\r\n");
        header.Append("Content-Type: application/xml\r\n");
        header.Append($"Content-Length: {bodyBytes.Length}\r\n");
        if (lastModifiedUtc.HasValue)
        {
            header.Append($"Last-Modified: {lastModifiedUtc.Value.ToUniversalTime():R}\r\n");
        }

        header.Append("Connection: close\r\n\r\n");
        byte[] headerBytes = Encoding.ASCII.GetBytes(header.ToString());

        byte[] response = new byte[headerBytes.Length + bodyBytes.Length];
        Buffer.BlockCopy(headerBytes, 0, response, 0, headerBytes.Length);
        Buffer.BlockCopy(bodyBytes, 0, response, headerBytes.Length, bodyBytes.Length);
        return response;
    }
}
