using ScadaLink.Commons.Entities.Audit;
using ScadaLink.Commons.Types.Enums;

namespace ScadaLink.Commons.Tests.Entities.Audit;

/// <summary>
/// Verifies <see cref="AuditEvent"/> behaves as an init-only record:
/// every property reads back as constructed, and <c>with</c> expressions
/// produce a new instance with a single property changed.
/// </summary>
public class AuditEventTests
{
    [Fact]
    public void Construction_AllPropertiesReadBack()
    {
        var eventId = Guid.NewGuid();
        var occurredAt = new DateTime(2026, 5, 20, 12, 0, 0, DateTimeKind.Utc);
        var ingestedAt = new DateTime(2026, 5, 20, 12, 0, 1, DateTimeKind.Utc);
        var corrId = Guid.NewGuid();

        var evt = new AuditEvent
        {
            EventId = eventId,
            OccurredAtUtc = occurredAt,
            IngestedAtUtc = ingestedAt,
            Channel = AuditChannel.ApiOutbound,
            Kind = AuditKind.ApiCall,
            CorrelationId = corrId,
            SourceSiteId = "site-01",
            SourceInstanceId = "inst-7",
            SourceScript = "OnAlarm",
            Actor = "system",
            Target = "WeatherAPI",
            Status = AuditStatus.Delivered,
            HttpStatus = 200,
            DurationMs = 42,
            ErrorMessage = null,
            ErrorDetail = null,
            RequestSummary = "GET /forecast",
            ResponseSummary = "{\"temp\":21}",
            PayloadTruncated = false,
            Extra = "{}",
            ForwardState = AuditForwardState.Forwarded
        };

        Assert.Equal(eventId, evt.EventId);
        Assert.Equal(occurredAt, evt.OccurredAtUtc);
        Assert.Equal(ingestedAt, evt.IngestedAtUtc);
        Assert.Equal(AuditChannel.ApiOutbound, evt.Channel);
        Assert.Equal(AuditKind.ApiCall, evt.Kind);
        Assert.Equal(corrId, evt.CorrelationId);
        Assert.Equal("site-01", evt.SourceSiteId);
        Assert.Equal("inst-7", evt.SourceInstanceId);
        Assert.Equal("OnAlarm", evt.SourceScript);
        Assert.Equal("system", evt.Actor);
        Assert.Equal("WeatherAPI", evt.Target);
        Assert.Equal(AuditStatus.Delivered, evt.Status);
        Assert.Equal(200, evt.HttpStatus);
        Assert.Equal(42, evt.DurationMs);
        Assert.Null(evt.ErrorMessage);
        Assert.Null(evt.ErrorDetail);
        Assert.Equal("GET /forecast", evt.RequestSummary);
        Assert.Equal("{\"temp\":21}", evt.ResponseSummary);
        Assert.False(evt.PayloadTruncated);
        Assert.Equal("{}", evt.Extra);
        Assert.Equal(AuditForwardState.Forwarded, evt.ForwardState);
    }

    [Fact]
    public void NullableProperties_AcceptNull()
    {
        var evt = new AuditEvent
        {
            EventId = Guid.NewGuid(),
            OccurredAtUtc = DateTime.UtcNow,
            IngestedAtUtc = null,
            Channel = AuditChannel.Notification,
            Kind = AuditKind.NotifySend,
            CorrelationId = null,
            SourceSiteId = null,
            SourceInstanceId = null,
            SourceScript = null,
            Actor = null,
            Target = null,
            Status = AuditStatus.Submitted,
            HttpStatus = null,
            DurationMs = null,
            ErrorMessage = null,
            ErrorDetail = null,
            RequestSummary = null,
            ResponseSummary = null,
            PayloadTruncated = false,
            Extra = null,
            ForwardState = null
        };

        Assert.Null(evt.IngestedAtUtc);
        Assert.Null(evt.CorrelationId);
        Assert.Null(evt.SourceSiteId);
        Assert.Null(evt.SourceInstanceId);
        Assert.Null(evt.SourceScript);
        Assert.Null(evt.Actor);
        Assert.Null(evt.Target);
        Assert.Null(evt.HttpStatus);
        Assert.Null(evt.DurationMs);
        Assert.Null(evt.ErrorMessage);
        Assert.Null(evt.ErrorDetail);
        Assert.Null(evt.RequestSummary);
        Assert.Null(evt.ResponseSummary);
        Assert.Null(evt.Extra);
        Assert.Null(evt.ForwardState);
    }

    [Fact]
    public void With_ProducesNewInstance_WithSingleFieldChanged()
    {
        var original = new AuditEvent
        {
            EventId = Guid.NewGuid(),
            OccurredAtUtc = DateTime.UtcNow,
            Channel = AuditChannel.ApiOutbound,
            Kind = AuditKind.ApiCall,
            Status = AuditStatus.Submitted,
            PayloadTruncated = false
        };

        var updated = original with { Status = AuditStatus.Delivered };

        Assert.NotSame(original, updated);
        Assert.Equal(AuditStatus.Submitted, original.Status);
        Assert.Equal(AuditStatus.Delivered, updated.Status);
        Assert.Equal(original.EventId, updated.EventId);
        Assert.NotEqual(original, updated);
    }
}
