using System.Collections.ObjectModel;
using System.Text;

namespace NATS.Server.Protocol;

public readonly struct NatsHeaders()
{
    public int Status { get; init; }
    public string Description { get; init; } = string.Empty;
    public IReadOnlyDictionary<string, string[]> Headers { get; init; } = ReadOnlyDictionary<string, string[]>.Empty;

    public static readonly NatsHeaders Invalid = new()
    {
        Status = -1,
        Description = string.Empty,
        Headers = ReadOnlyDictionary<string, string[]>.Empty,
    };
}

public static class NatsHeaderParser
{
    private static ReadOnlySpan<byte> CrLf => "\r\n"u8;
    private static ReadOnlySpan<byte> Prefix => "NATS/1.0"u8;

    public static NatsHeaders Parse(ReadOnlySpan<byte> data)
    {
        if (data.Length < Prefix.Length)
            return NatsHeaders.Invalid;

        if (!data[..Prefix.Length].SequenceEqual(Prefix))
            return NatsHeaders.Invalid;

        int pos = Prefix.Length;
        int status = 0;
        string description = string.Empty;

        // Parse status line: NATS/1.0[ status[ description]]\r\n
        int lineEnd = data[pos..].IndexOf(CrLf);
        if (lineEnd < 0)
            return NatsHeaders.Invalid;

        var statusLine = data[pos..(pos + lineEnd)];
        pos += lineEnd + 2; // skip \r\n

        if (statusLine.Length > 0)
        {
            int si = 0;
            while (si < statusLine.Length && statusLine[si] == (byte)' ')
                si++;

            int numStart = si;
            while (si < statusLine.Length && statusLine[si] >= (byte)'0' && statusLine[si] <= (byte)'9')
                si++;

            if (si > numStart && si - numStart <= 5) // max 5 digits to avoid overflow
            {
                for (int idx = numStart; idx < si; idx++)
                    status = status * 10 + (statusLine[idx] - '0');

                while (si < statusLine.Length && statusLine[si] == (byte)' ')
                    si++;
                if (si < statusLine.Length)
                    description = Encoding.ASCII.GetString(statusLine[si..]);
            }
        }

        // Parse key-value headers until empty line
        var headers = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
        while (pos < data.Length)
        {
            var remaining = data[pos..];
            if (remaining.Length >= 2 && remaining[0] == (byte)'\r' && remaining[1] == (byte)'\n')
                break;

            lineEnd = remaining.IndexOf(CrLf);
            if (lineEnd < 0)
                break;

            var headerLine = remaining[..lineEnd];
            pos += lineEnd + 2;

            int colon = headerLine.IndexOf((byte)':');
            if (colon < 0)
                continue;

            var key = Encoding.ASCII.GetString(headerLine[..colon]).Trim();
            var value = Encoding.ASCII.GetString(headerLine[(colon + 1)..]).Trim();

            if (!headers.TryGetValue(key, out var values))
            {
                values = [];
                headers[key] = values;
            }
            values.Add(value);
        }

        var result = new Dictionary<string, string[]>(headers.Count, StringComparer.OrdinalIgnoreCase);
        foreach (var (k, v) in headers)
            result[k] = v.ToArray();

        return new NatsHeaders
        {
            Status = status,
            Description = description,
            Headers = result,
        };
    }
}
