using System.Diagnostics;
using Npgsql.Internal;

namespace Npgsql.BackendMessages;

class CommandCompleteMessage : IBackendMessage
{
    internal StatementType StatementType { get; private set; }
    internal uint OID { get; private set; }
    internal ulong Rows { get; private set; }

    internal CommandCompleteMessage Load(NpgsqlReadBuffer buf, int len)
    {
        Rows = 0;
        OID = 0;

        var bytes = buf.Buffer;
        var i = buf.ReadPosition;
        buf.Skip(len);
        switch (bytes[i])
        {
        case (byte)'I':
            if (!AreEqual(bytes, i, "INSERT "))
                goto default;
            StatementType = StatementType.Insert;
            i += 7;
            OID = (uint) ParseNumber(bytes, ref i);
            i++;
            Rows = ParseNumber(bytes, ref i);
            return this;

        case (byte)'D':
            if (!AreEqual(bytes, i, "DELETE "))
                goto default;
            StatementType = StatementType.Delete;
            i += 7;
            Rows = ParseNumber(bytes, ref i);
            return this;

        case (byte)'U':
            if (!AreEqual(bytes, i, "UPDATE "))
                goto default;
            StatementType = StatementType.Update;
            i += 7;
            Rows = ParseNumber(bytes, ref i);
            return this;

        case (byte)'S':
            if (!AreEqual(bytes, i, "SELECT "))
                goto default;
            StatementType = StatementType.Select;
            i += 7;
            Rows = ParseNumber(bytes, ref i);
            return this;

        case (byte)'M':
            if (!AreEqual(bytes, i, "MOVE "))
                goto default;
            StatementType = StatementType.Move;
            i += 5;
            Rows = ParseNumber(bytes, ref i);
            return this;

        case (byte)'F':
            if (!AreEqual(bytes, i, "FETCH "))
                goto default;
            StatementType = StatementType.Fetch;
            i += 6;
            Rows = ParseNumber(bytes, ref i);
            return this;

        case (byte)'C':
            if (!AreEqual(bytes, i, "COPY "))
                goto default;
            StatementType = StatementType.Copy;
            i += 5;
            Rows = ParseNumber(bytes, ref i);
            return this;

        default:
            StatementType = StatementType.Other;
            return this;
        }
    }

    static bool AreEqual(byte[] bytes, int pos, string s)
    {
        for (var i = 0; i < s.Length; i++)
        {
            if (bytes[pos+i] != s[i])
                return false;
        }
        return true;
    }

    static ulong ParseNumber(byte[] bytes, ref int pos)
    {
        Debug.Assert(bytes[pos] >= '0' && bytes[pos] <= '9');
        ulong result = 0;
        do
        {
            result = result * 10 + bytes[pos++] - '0';
        } while (bytes[pos] >= '0' && bytes[pos] <= '9');
        return result;
    }

    public BackendMessageCode Code => BackendMessageCode.CommandComplete;
}