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

namespace NATS.Server.Mqtt.Tests.Mqtt;

public class MqttProtocolConstantsParityBatch2Tests
{
    [Fact]
    public void Extended_constants_match_go_reference_values()
    {
        MqttProtocolConstants.MultiLevelSidSuffix.ShouldBe(" fwc");
        MqttProtocolConstants.Prefix.ShouldBe("$MQTT.");
        MqttProtocolConstants.SubPrefix.ShouldBe("$MQTT.sub.");

        MqttProtocolConstants.StreamName.ShouldBe("$MQTT_msgs");
        MqttProtocolConstants.StreamSubjectPrefix.ShouldBe("$MQTT.msgs.");
        MqttProtocolConstants.RetainedMsgsStreamName.ShouldBe("$MQTT_rmsgs");
        MqttProtocolConstants.RetainedMsgsStreamSubject.ShouldBe("$MQTT.rmsgs.");
        MqttProtocolConstants.SessStreamName.ShouldBe("$MQTT_sess");
        MqttProtocolConstants.SessStreamSubjectPrefix.ShouldBe("$MQTT.sess.");
        MqttProtocolConstants.SessionsStreamNamePrefix.ShouldBe("$MQTT_sess_");
        MqttProtocolConstants.QoS2IncomingMsgsStreamName.ShouldBe("$MQTT_qos2in");
        MqttProtocolConstants.QoS2IncomingMsgsStreamSubjectPrefix.ShouldBe("$MQTT.qos2.in.");

        MqttProtocolConstants.OutStreamName.ShouldBe("$MQTT_out");
        MqttProtocolConstants.OutSubjectPrefix.ShouldBe("$MQTT.out.");
        MqttProtocolConstants.PubRelSubjectPrefix.ShouldBe("$MQTT.out.pubrel.");
        MqttProtocolConstants.PubRelDeliverySubjectPrefix.ShouldBe("$MQTT.deliver.pubrel.");
        MqttProtocolConstants.PubRelConsumerDurablePrefix.ShouldBe("$MQTT_PUBREL_");

        MqttProtocolConstants.JSARepliesPrefix.ShouldBe("$MQTT.JSA.");
        MqttProtocolConstants.JSAIdTokenPos.ShouldBe(3);
        MqttProtocolConstants.JSATokenPos.ShouldBe(4);
        MqttProtocolConstants.JSAClientIDPos.ShouldBe(5);
        MqttProtocolConstants.JSAStreamCreate.ShouldBe("SC");
        MqttProtocolConstants.JSAStreamUpdate.ShouldBe("SU");
        MqttProtocolConstants.JSAStreamLookup.ShouldBe("SL");
        MqttProtocolConstants.JSAStreamDel.ShouldBe("SD");
        MqttProtocolConstants.JSAConsumerCreate.ShouldBe("CC");
        MqttProtocolConstants.JSAConsumerLookup.ShouldBe("CL");
        MqttProtocolConstants.JSAConsumerDel.ShouldBe("CD");
        MqttProtocolConstants.JSAMsgStore.ShouldBe("MS");
        MqttProtocolConstants.JSAMsgLoad.ShouldBe("ML");
        MqttProtocolConstants.JSAMsgDelete.ShouldBe("MD");
        MqttProtocolConstants.JSASessPersist.ShouldBe("SP");
        MqttProtocolConstants.JSARetainedMsgDel.ShouldBe("RD");
        MqttProtocolConstants.JSAStreamNames.ShouldBe("SN");

        MqttProtocolConstants.SparkbNBirth.ShouldBe("NBIRTH");
        MqttProtocolConstants.SparkbDBirth.ShouldBe("DBIRTH");
        MqttProtocolConstants.SparkbNDeath.ShouldBe("NDEATH");
        MqttProtocolConstants.SparkbDDeath.ShouldBe("DDEATH");
        Encoding.ASCII.GetString(MqttProtocolConstants.SparkbNamespaceTopicPrefix).ShouldBe("spBv1.0/");
        Encoding.ASCII.GetString(MqttProtocolConstants.SparkbCertificatesTopicPrefix).ShouldBe("$sparkplug/certificates/");

        MqttProtocolConstants.NatsHeaderPublish.ShouldBe("Nmqtt-Pub");
        MqttProtocolConstants.NatsRetainedMessageTopic.ShouldBe("Nmqtt-RTopic");
        MqttProtocolConstants.NatsRetainedMessageOrigin.ShouldBe("Nmqtt-ROrigin");
        MqttProtocolConstants.NatsRetainedMessageFlags.ShouldBe("Nmqtt-RFlags");
        MqttProtocolConstants.NatsRetainedMessageSource.ShouldBe("Nmqtt-RSource");
        MqttProtocolConstants.NatsPubRelHeader.ShouldBe("Nmqtt-PubRel");
        MqttProtocolConstants.NatsHeaderSubject.ShouldBe("Nmqtt-Subject");
        MqttProtocolConstants.NatsHeaderMapped.ShouldBe("Nmqtt-Mapped");
    }

    [Fact]
    public void WriteString_writes_length_prefixed_utf8()
    {
        var encoded = MqttPacketWriter.WriteString("MQTT");

        encoded.Length.ShouldBe(6);
        encoded[0].ShouldBe((byte)0x00);
        encoded[1].ShouldBe((byte)0x04);
        Encoding.UTF8.GetString(encoded.AsSpan(2)).ShouldBe("MQTT");
    }

    [Fact]
    public void WriteBytes_writes_length_prefixed_binary_payload()
    {
        var encoded = MqttPacketWriter.WriteBytes(new byte[] { 0xAA, 0xBB, 0xCC });

        encoded.ShouldBe(new byte[] { 0x00, 0x03, 0xAA, 0xBB, 0xCC });
    }

    [Fact]
    public void WriteBytes_rejects_payload_larger_than_uint16()
    {
        var payload = new byte[ushort.MaxValue + 1];
        Should.Throw<ArgumentOutOfRangeException>(() => MqttPacketWriter.WriteBytes(payload));
    }
}
