using System;
using System.IO;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Stability;

namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Tests
{
    [Trait("Category", "Unit")]
    public sealed class PostMortemMmfTests : IDisposable
    {
        private readonly string _tempPath;

        public PostMortemMmfTests()
        {
            _tempPath = Path.Combine(Path.GetTempPath(), $"focas-mmf-{Guid.NewGuid():N}.bin");
        }

        public void Dispose()
        {
            if (File.Exists(_tempPath)) File.Delete(_tempPath);
        }

        [Fact]
        public void Write_and_read_preserve_order_and_content()
        {
            using (var mmf = new PostMortemMmf(_tempPath, capacity: 10))
            {
                mmf.Write(opKind: 1, "read R100");
                mmf.Write(opKind: 2, "write MACRO:500 = 3.14");
                mmf.Write(opKind: 3, "probe ok");
            }

            // Reopen (simulating a reader after the writer crashed).
            using var reader = new PostMortemMmf(_tempPath, capacity: 10);
            var entries = reader.ReadAll();
            entries.Length.ShouldBe(3);
            entries[0].OpKind.ShouldBe(1L);
            entries[0].Message.ShouldBe("read R100");
            entries[1].OpKind.ShouldBe(2L);
            entries[2].Message.ShouldBe("probe ok");
        }

        [Fact]
        public void Ring_buffer_wraps_at_capacity()
        {
            using var mmf = new PostMortemMmf(_tempPath, capacity: 3);
            for (var i = 0; i < 10; i++) mmf.Write(i, $"op-{i}");

            var entries = mmf.ReadAll();
            entries.Length.ShouldBe(3);
            // Oldest surviving entry is op-7 (entries 7,8,9 survive in FIFO order).
            entries[0].Message.ShouldBe("op-7");
            entries[1].Message.ShouldBe("op-8");
            entries[2].Message.ShouldBe("op-9");
        }

        [Fact]
        public void Truncated_message_is_null_terminated_and_does_not_overflow()
        {
            using var mmf = new PostMortemMmf(_tempPath, capacity: 4);
            var big = new string('x', 500); // longer than the 240-byte message capacity
            mmf.Write(42, big);

            var entries = mmf.ReadAll();
            entries.Length.ShouldBe(1);
            entries[0].Message.Length.ShouldBeLessThanOrEqualTo(240);
            entries[0].OpKind.ShouldBe(42L);
        }

        [Fact]
        public void Reopening_with_existing_data_preserves_entries()
        {
            using (var first = new PostMortemMmf(_tempPath, capacity: 5))
            {
                first.Write(1, "first-run-1");
                first.Write(2, "first-run-2");
            }

            using var second = new PostMortemMmf(_tempPath, capacity: 5);
            var entries = second.ReadAll();
            entries.Length.ShouldBe(2);
            entries[0].Message.ShouldBe("first-run-1");
        }
    }
}
