using System.Text;
using NATS.Server.Mqtt;

namespace NATS.Server.Mqtt.Tests.Mqtt;

public class MqttProtocolConstantsParityBatch1Tests
{
    [Fact]
    public void Constants_match_mqtt_go_reference_values()
    {
        MqttProtocolConstants.SubscribeFlags.ShouldBe((byte)0x02);

        MqttProtocolConstants.ConnAckAccepted.ShouldBe((byte)0x00);
        MqttProtocolConstants.ConnAckUnacceptableProtocolVersion.ShouldBe((byte)0x01);
        MqttProtocolConstants.ConnAckIdentifierRejected.ShouldBe((byte)0x02);
        MqttProtocolConstants.ConnAckServerUnavailable.ShouldBe((byte)0x03);
        MqttProtocolConstants.ConnAckBadUserNameOrPassword.ShouldBe((byte)0x04);
        MqttProtocolConstants.ConnAckNotAuthorized.ShouldBe((byte)0x05);

        MqttProtocolConstants.MaxPayloadSize.ShouldBe(268_435_455);
        MqttProtocolConstants.DefaultAckWait.ShouldBe(TimeSpan.FromSeconds(30));
        MqttProtocolConstants.MaxAckTotalLimit.ShouldBe(0xFFFF);
    }

    [Fact]
    public void ParseSubscribe_accepts_required_subscribe_flags()
    {
        var payload = CreateSubscribePayload(packetId: 7, ("sport/tennis/#", 1));

        var info = MqttBinaryDecoder.ParseSubscribe(payload, flags: MqttProtocolConstants.SubscribeFlags);

        info.PacketId.ShouldBe((ushort)7);
        info.Filters.Count.ShouldBe(1);
        info.Filters[0].TopicFilter.ShouldBe("sport/tennis/#");
        info.Filters[0].QoS.ShouldBe((byte)1);
    }

    [Fact]
    public void ParseSubscribe_rejects_invalid_subscribe_flags()
    {
        var payload = CreateSubscribePayload(packetId: 5, ("topic/one", 0));

        var ex = Should.Throw<FormatException>(() => MqttBinaryDecoder.ParseSubscribe(payload, flags: 0x00));
        ex.Message.ShouldContain("invalid fixed-header flags");
    }

    private static byte[] CreateSubscribePayload(ushort packetId, params (string Topic, byte Qos)[] filters)
    {
        using var ms = new MemoryStream();
        using var writer = new BinaryWriter(ms);

        WriteUInt16BigEndian(writer, packetId);
        foreach (var (topic, qos) in filters)
        {
            WriteString(writer, topic);
            writer.Write(qos);
        }

        return ms.ToArray();
    }

    private static void WriteString(BinaryWriter writer, string value)
    {
        var bytes = Encoding.UTF8.GetBytes(value);
        WriteUInt16BigEndian(writer, (ushort)bytes.Length);
        writer.Write(bytes);
    }

    private static void WriteUInt16BigEndian(BinaryWriter writer, ushort value)
    {
        writer.Write((byte)(value >> 8));
        writer.Write((byte)(value & 0xFF));
    }
}
