namespace NATS.Server.Mqtt;

public enum MqttPacketType
{
    Unknown,
    Connect,
    Subscribe,
    Publish,
    PublishQos1,
    Ack,
}

public sealed record MqttPacket(
    MqttPacketType Type,
    string Topic,
    string Payload,
    string ClientId,
    int PacketId = 0,
    bool CleanSession = true,
    string? Username = null,
    string? Password = null,
    int KeepAliveSeconds = 0);

public sealed class MqttProtocolParser
{
    public MqttControlPacket ParsePacket(ReadOnlySpan<byte> packet)
        => MqttPacketReader.Read(packet);

    public byte[] WritePacket(MqttControlPacketType type, ReadOnlySpan<byte> payload, byte flags = 0)
        => MqttPacketWriter.Write(type, payload, flags);

    public MqttPacket ParseLine(string line)
    {
        var trimmed = line.Trim();
        if (trimmed.Length == 0)
            return new MqttPacket(MqttPacketType.Unknown, string.Empty, string.Empty, string.Empty);

        if (trimmed.StartsWith("CONNECT ", StringComparison.Ordinal))
        {
            var parts = trimmed.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length < 2)
                return new MqttPacket(MqttPacketType.Unknown, string.Empty, string.Empty, string.Empty);

            var cleanSession = true;
            string? username = null;
            string? password = null;
            var keepAliveSeconds = 0;
            for (var i = 2; i < parts.Length; i++)
            {
                if (parts[i].StartsWith("clean=", StringComparison.OrdinalIgnoreCase)
                    && bool.TryParse(parts[i]["clean=".Length..], out var parsedClean))
                {
                    cleanSession = parsedClean;
                }

                if (parts[i].StartsWith("user=", StringComparison.OrdinalIgnoreCase))
                    username = parts[i]["user=".Length..];

                if (parts[i].StartsWith("pass=", StringComparison.OrdinalIgnoreCase))
                    password = parts[i]["pass=".Length..];

                if (parts[i].StartsWith("keepalive=", StringComparison.OrdinalIgnoreCase)
                    && int.TryParse(parts[i]["keepalive=".Length..], out var parsedKeepAlive)
                    && parsedKeepAlive >= 0)
                {
                    keepAliveSeconds = parsedKeepAlive;
                }
            }

            return new MqttPacket(
                MqttPacketType.Connect,
                string.Empty,
                string.Empty,
                parts[1],
                CleanSession: cleanSession,
                Username: username,
                Password: password,
                KeepAliveSeconds: keepAliveSeconds);
        }

        if (trimmed.StartsWith("SUB ", StringComparison.Ordinal))
        {
            return new MqttPacket(
                MqttPacketType.Subscribe,
                trimmed["SUB ".Length..].Trim(),
                string.Empty,
                string.Empty);
        }

        if (trimmed.StartsWith("PUB ", StringComparison.Ordinal))
        {
            var rest = trimmed["PUB ".Length..];
            var sep = rest.IndexOf(' ');
            if (sep <= 0)
                return new MqttPacket(MqttPacketType.Unknown, string.Empty, string.Empty, string.Empty);

            var topic = rest[..sep].Trim();
            var payload = rest[(sep + 1)..];
            return new MqttPacket(MqttPacketType.Publish, topic, payload, string.Empty);
        }

        if (trimmed.StartsWith("PUBQ1 ", StringComparison.Ordinal))
        {
            var rest = trimmed["PUBQ1 ".Length..];
            var firstSep = rest.IndexOf(' ');
            if (firstSep <= 0)
                return new MqttPacket(MqttPacketType.Unknown, string.Empty, string.Empty, string.Empty);

            var secondSep = rest.IndexOf(' ', firstSep + 1);
            if (secondSep <= firstSep + 1)
                return new MqttPacket(MqttPacketType.Unknown, string.Empty, string.Empty, string.Empty);

            if (!int.TryParse(rest[..firstSep], out var packetId) || packetId <= 0)
                return new MqttPacket(MqttPacketType.Unknown, string.Empty, string.Empty, string.Empty);

            var topic = rest[(firstSep + 1)..secondSep].Trim();
            var payload = rest[(secondSep + 1)..];
            return new MqttPacket(
                MqttPacketType.PublishQos1,
                topic,
                payload,
                string.Empty,
                PacketId: packetId);
        }

        if (trimmed.StartsWith("ACK ", StringComparison.Ordinal))
        {
            if (!int.TryParse(trimmed["ACK ".Length..].Trim(), out var packetId) || packetId <= 0)
                return new MqttPacket(MqttPacketType.Unknown, string.Empty, string.Empty, string.Empty);

            return new MqttPacket(
                MqttPacketType.Ack,
                string.Empty,
                string.Empty,
                string.Empty,
                PacketId: packetId);
        }

        return new MqttPacket(MqttPacketType.Unknown, string.Empty, string.Empty, string.Empty);
    }
}
