namespace NATS.Server.Mqtt;

public enum MqttControlPacketType : byte
{
    Reserved = 0,
    Connect = 1,
    ConnAck = 2,
    Publish = 3,
    PubAck = 4,
    Subscribe = 8,
    SubAck = 9,
    PingReq = 12,
    PingResp = 13,
    Disconnect = 14,
}

public sealed record MqttControlPacket(
    MqttControlPacketType Type,
    byte Flags,
    int RemainingLength,
    ReadOnlyMemory<byte> Payload);

public static class MqttPacketReader
{
    public static MqttControlPacket Read(ReadOnlySpan<byte> buffer)
    {
        if (buffer.Length < 2)
            throw new FormatException("MQTT packet is shorter than fixed header.");

        var first = buffer[0];
        var type = (MqttControlPacketType)(first >> 4);
        var flags = (byte)(first & 0x0F);
        var remainingLength = DecodeRemainingLength(buffer[1..], out var consumed);
        if (remainingLength > MqttProtocolConstants.MaxPayloadSize)
            throw new FormatException("MQTT packet remaining length exceeds protocol maximum.");
        var payloadStart = 1 + consumed;
        var totalLength = payloadStart + remainingLength;
        if (remainingLength < 0 || totalLength > buffer.Length)
            throw new FormatException("MQTT packet remaining length exceeds available bytes.");

        var payload = buffer[payloadStart..totalLength].ToArray();
        return new MqttControlPacket(type, flags, remainingLength, payload);
    }

    internal static int DecodeRemainingLength(ReadOnlySpan<byte> encoded, out int consumed)
    {
        var multiplier = 1;
        var value = 0;
        consumed = 0;

        for (var i = 0; i < encoded.Length && i < 4; i++)
        {
            var digit = encoded[i];
            consumed++;
            value += (digit & 0x7F) * multiplier;

            if ((digit & 0x80) == 0)
                return value;

            multiplier *= 128;
        }

        throw new FormatException("Invalid MQTT remaining length encoding.");
    }
}
