using System.Text;

namespace DbaMessageRestorer;

public sealed record DbaDecodedMessage(
    byte[] MessageBytes,
    string? Message,
    bool IsValidUtf8,
    bool TerminatorFound,
    int WholeDbaByteCount,
    string? Error);

/// <summary>
/// Reconnects DBA bits in physical NPNCB order, groups complete bytes LSB-first,
/// stops at the first zero-byte terminator, and decodes the result as strict UTF-8.
/// </summary>
public static class DbaMessageDecoder
{
    private static readonly UTF8Encoding StrictUtf8 = new(false, true);

    public static DbaDecodedMessage Decode(IReadOnlyList<byte> dbaBits)
    {
        if (dbaBits is null) throw new ArgumentNullException(nameof(dbaBits));

        int wholeByteCount = dbaBits.Count / 8;
        var allWholeBytes = new byte[wholeByteCount];

        for (int byteIndex = 0; byteIndex < wholeByteCount; byteIndex++)
        {
            byte value = 0;
            for (int bitIndex = 0; bitIndex < 8; bitIndex++)
            {
                byte bit = dbaBits[byteIndex * 8 + bitIndex];
                if (bit > 1)
                    throw new ArgumentException("DBA bit values must be 0 or 1.", nameof(dbaBits));
                value |= (byte)(bit << bitIndex);
            }
            allWholeBytes[byteIndex] = value;
        }

        int terminatorIndex = Array.IndexOf(allWholeBytes, (byte)0);
        bool terminatorFound = terminatorIndex >= 0;
        int messageLength = terminatorFound ? terminatorIndex : allWholeBytes.Length;
        byte[] messageBytes = allWholeBytes[..messageLength];

        try
        {
            return new DbaDecodedMessage(
                messageBytes,
                StrictUtf8.GetString(messageBytes),
                true,
                terminatorFound,
                wholeByteCount,
                null);
        }
        catch (DecoderFallbackException exception)
        {
            return new DbaDecodedMessage(
                messageBytes,
                null,
                false,
                terminatorFound,
                wholeByteCount,
                exception.Message);
        }
    }
}
