using System.IO.MemoryMappedFiles;
using System.Text;

namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Supervisor;

/// <summary>
///     Proxy-side reader for the Host's post-mortem MMF. After a Host crash the supervisor
///     opens the file (which persists beyond the process lifetime) and enumerates the last
///     few thousand IPC operations that were in flight. Format matches
///     <c>Driver.FOCAS.Host.Stability.PostMortemMmf</c> — magic 'OFPC' / 256-byte entries.
/// </summary>
public sealed class PostMortemReader
{
    private const int Magic = 0x4F465043; // 'OFPC'
    private const int HeaderBytes = 16;
    private const int EntryBytes = 256;
    private const int MessageOffset = 16;
    private const int MessageCapacity = EntryBytes - MessageOffset;

    public string Path { get; }

    public PostMortemReader(string path) => Path = path;

    public PostMortemEntry[] ReadAll()
    {
        if (!File.Exists(Path)) return [];

        using var mmf = MemoryMappedFile.CreateFromFile(Path, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
        using var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);

        if (accessor.ReadInt32(0) != Magic) return [];

        var capacity = accessor.ReadInt32(8);
        var writeIndex = accessor.ReadInt32(12);
        var entries = new PostMortemEntry[capacity];
        var count = 0;

        for (var i = 0; i < capacity; i++)
        {
            var slot = (writeIndex + i) % capacity;
            var offset = HeaderBytes + slot * EntryBytes;
            var ts = accessor.ReadInt64(offset + 0);
            if (ts == 0) continue;
            var op = accessor.ReadInt64(offset + 8);
            var msgBuf = new byte[MessageCapacity];
            accessor.ReadArray(offset + MessageOffset, msgBuf, 0, MessageCapacity);
            var nulTerm = Array.IndexOf<byte>(msgBuf, 0);
            var msg = Encoding.UTF8.GetString(msgBuf, 0, nulTerm < 0 ? MessageCapacity : nulTerm);
            entries[count++] = new PostMortemEntry(ts, op, msg);
        }

        Array.Resize(ref entries, count);
        return entries;
    }
}

public readonly record struct PostMortemEntry(long UtcUnixMs, long OpKind, string Message);
