using ZB.MOM.WW.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;

namespace ZB.MOM.WW.ScadaBridge.Commons.Tests.Types.Audit;

/// <summary>
/// C1 projection tests: the full AuditStatus × AuditKind → AuditOutcome table, plus
/// the InboundAuthFailure-Denied precedence that must be checked before any status rule.
/// </summary>
public class AuditOutcomeProjectorTests
{
    // ---------------------------------------------------------------------------
    // InboundAuthFailure → Denied, regardless of status (checked FIRST)
    // ---------------------------------------------------------------------------

    [Theory]
    [InlineData(AuditStatus.Submitted)]
    [InlineData(AuditStatus.Forwarded)]
    [InlineData(AuditStatus.Attempted)]
    [InlineData(AuditStatus.Delivered)]
    [InlineData(AuditStatus.Failed)]
    [InlineData(AuditStatus.Parked)]
    [InlineData(AuditStatus.Discarded)]
    [InlineData(AuditStatus.Skipped)]
    public void InboundAuthFailure_AlwaysDenied_RegardlessOfStatus(AuditStatus status)
    {
        var outcome = AuditOutcomeProjector.Project(status, AuditKind.InboundAuthFailure);
        Assert.Equal(AuditOutcome.Denied, outcome);
    }

    // ---------------------------------------------------------------------------
    // Delivered → Success (for all non-auth-failure kinds)
    // ---------------------------------------------------------------------------

    [Theory]
    [InlineData(AuditKind.ApiCall)]
    [InlineData(AuditKind.ApiCallCached)]
    [InlineData(AuditKind.DbWrite)]
    [InlineData(AuditKind.DbWriteCached)]
    [InlineData(AuditKind.NotifySend)]
    [InlineData(AuditKind.NotifyDeliver)]
    [InlineData(AuditKind.InboundRequest)]
    [InlineData(AuditKind.CachedSubmit)]
    [InlineData(AuditKind.CachedResolve)]
    public void Delivered_Success_ForNonAuthFailureKinds(AuditKind kind)
    {
        var outcome = AuditOutcomeProjector.Project(AuditStatus.Delivered, kind);
        Assert.Equal(AuditOutcome.Success, outcome);
    }

    // ---------------------------------------------------------------------------
    // Failure statuses → Failure outcome
    // ---------------------------------------------------------------------------

    [Theory]
    [InlineData(AuditStatus.Failed)]
    [InlineData(AuditStatus.Parked)]
    [InlineData(AuditStatus.Discarded)]
    public void FailureStatuses_MapToFailureOutcome(AuditStatus status)
    {
        // Use a representative non-auth-failure kind
        var outcome = AuditOutcomeProjector.Project(status, AuditKind.ApiCall);
        Assert.Equal(AuditOutcome.Failure, outcome);
    }

    // InboundAuthFailure still wins over failure statuses
    [Theory]
    [InlineData(AuditStatus.Failed)]
    [InlineData(AuditStatus.Parked)]
    [InlineData(AuditStatus.Discarded)]
    public void InboundAuthFailure_OverridesFailureStatuses(AuditStatus status)
    {
        var outcome = AuditOutcomeProjector.Project(status, AuditKind.InboundAuthFailure);
        Assert.Equal(AuditOutcome.Denied, outcome);
    }

    // ---------------------------------------------------------------------------
    // In-progress / short-circuit statuses → Success
    // ---------------------------------------------------------------------------

    [Theory]
    [InlineData(AuditStatus.Submitted)]
    [InlineData(AuditStatus.Forwarded)]
    [InlineData(AuditStatus.Attempted)]
    [InlineData(AuditStatus.Skipped)]
    public void InProgressStatuses_MapToSuccess(AuditStatus status)
    {
        var outcome = AuditOutcomeProjector.Project(status, AuditKind.ApiCall);
        Assert.Equal(AuditOutcome.Success, outcome);
    }

    // ---------------------------------------------------------------------------
    // Full status × non-auth-failure kind cross-product (spot-check each status once more)
    // ---------------------------------------------------------------------------

    [Theory]
    [InlineData(AuditStatus.Submitted,  AuditKind.DbWrite,        AuditOutcome.Success)]
    [InlineData(AuditStatus.Forwarded,  AuditKind.CachedSubmit,   AuditOutcome.Success)]
    [InlineData(AuditStatus.Attempted,  AuditKind.NotifySend,     AuditOutcome.Success)]
    [InlineData(AuditStatus.Delivered,  AuditKind.NotifyDeliver,  AuditOutcome.Success)]
    [InlineData(AuditStatus.Failed,     AuditKind.DbWriteCached,  AuditOutcome.Failure)]
    [InlineData(AuditStatus.Parked,     AuditKind.NotifySend,     AuditOutcome.Failure)]
    [InlineData(AuditStatus.Discarded,  AuditKind.CachedResolve,  AuditOutcome.Failure)]
    [InlineData(AuditStatus.Skipped,    AuditKind.InboundRequest, AuditOutcome.Success)]
    public void ProjectionTable_MatchesSpec(AuditStatus status, AuditKind kind, AuditOutcome expected)
    {
        Assert.Equal(expected, AuditOutcomeProjector.Project(status, kind));
    }
}
