using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ZB.MOM.WW.ScadaBridge.Communication.Grpc;

namespace ZB.MOM.WW.ScadaBridge.Communication.Tests.Protos;

/// <summary>
/// Wire-format round-trip tests for the Audit Log (#23) telemetry proto messages
/// (<see cref="AuditEventDto"/>, <see cref="AuditEventBatch"/>, <see cref="IngestAck"/>).
/// Locks the additive contract the site → central audit pipeline depends on.
/// </summary>
public class AuditEventProtoTests
{
    [Fact]
    public void AuditEventDto_RoundTrip_PreservesAllFields()
    {
        var occurredAt = Timestamp.FromDateTimeOffset(
            new DateTimeOffset(2026, 5, 20, 10, 15, 30, 123, TimeSpan.Zero));

        var original = new AuditEventDto
        {
            EventId = Guid.NewGuid().ToString(),
            OccurredAtUtc = occurredAt,
            Channel = "ApiOutbound",
            Kind = "ApiCall",
            CorrelationId = Guid.NewGuid().ToString(),
            SourceSiteId = "site-1",
            SourceNode = "node-a",
            SourceInstanceId = "Pump01",
            SourceScript = "OnDemand",
            Actor = "design-key",
            Target = "weather-api",
            Status = "Delivered",
            HttpStatus = 200,
            DurationMs = 42,
            ErrorMessage = "no error",
            ErrorDetail = "stack",
            RequestSummary = "GET /weather?city=brisbane",
            ResponseSummary = "{ \"temp\": 22.5 }",
            PayloadTruncated = true,
            Extra = "{ \"retryCount\": 0 }"
        };

        var bytes = original.ToByteArray();
        var deserialized = AuditEventDto.Parser.ParseFrom(bytes);

        Assert.Equal(original.EventId, deserialized.EventId);
        Assert.Equal(original.OccurredAtUtc, deserialized.OccurredAtUtc);
        Assert.Equal(original.Channel, deserialized.Channel);
        Assert.Equal(original.Kind, deserialized.Kind);
        Assert.Equal(original.CorrelationId, deserialized.CorrelationId);
        Assert.Equal(original.SourceSiteId, deserialized.SourceSiteId);
        Assert.Equal(original.SourceNode, deserialized.SourceNode);
        Assert.Equal(original.SourceInstanceId, deserialized.SourceInstanceId);
        Assert.Equal(original.SourceScript, deserialized.SourceScript);
        Assert.Equal(original.Actor, deserialized.Actor);
        Assert.Equal(original.Target, deserialized.Target);
        Assert.Equal(original.Status, deserialized.Status);
        Assert.Equal(original.HttpStatus, deserialized.HttpStatus);
        Assert.Equal(original.DurationMs, deserialized.DurationMs);
        Assert.Equal(original.ErrorMessage, deserialized.ErrorMessage);
        Assert.Equal(original.ErrorDetail, deserialized.ErrorDetail);
        Assert.Equal(original.RequestSummary, deserialized.RequestSummary);
        Assert.Equal(original.ResponseSummary, deserialized.ResponseSummary);
        Assert.Equal(original.PayloadTruncated, deserialized.PayloadTruncated);
        Assert.Equal(original.Extra, deserialized.Extra);
    }

    [Fact]
    public void AuditEventDto_NullableInt_AbsentByDefault_NotIncludedInWire()
    {
        // Int32Value fields (http_status, duration_ms) are wrapper-typed in proto;
        // when unset, the wrapper is absent, not serialized, and deserializes back to null.
        var original = new AuditEventDto
        {
            EventId = Guid.NewGuid().ToString(),
            OccurredAtUtc = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
            Channel = "Notification",
            Kind = "NotifySend",
            Status = "Submitted"
        };

        Assert.Null(original.HttpStatus);
        Assert.Null(original.DurationMs);

        var bytes = original.ToByteArray();
        var deserialized = AuditEventDto.Parser.ParseFrom(bytes);

        Assert.Null(deserialized.HttpStatus);
        Assert.Null(deserialized.DurationMs);
    }

    [Fact]
    public void AuditEventBatch_Empty_RoundTrip_Yields_EmptyEvents()
    {
        var original = new AuditEventBatch();
        Assert.Empty(original.Events);

        var bytes = original.ToByteArray();
        var deserialized = AuditEventBatch.Parser.ParseFrom(bytes);

        Assert.Empty(deserialized.Events);
    }

    [Fact]
    public void IngestAck_PreservesAcceptedEventIds()
    {
        var id1 = Guid.NewGuid().ToString();
        var id2 = Guid.NewGuid().ToString();
        var id3 = Guid.NewGuid().ToString();

        var original = new IngestAck();
        original.AcceptedEventIds.Add(id1);
        original.AcceptedEventIds.Add(id2);
        original.AcceptedEventIds.Add(id3);

        var bytes = original.ToByteArray();
        var deserialized = IngestAck.Parser.ParseFrom(bytes);

        Assert.Equal(3, deserialized.AcceptedEventIds.Count);
        Assert.Equal(id1, deserialized.AcceptedEventIds[0]);
        Assert.Equal(id2, deserialized.AcceptedEventIds[1]);
        Assert.Equal(id3, deserialized.AcceptedEventIds[2]);
    }
}
