using KArtSell.Modules.ModelOperations.Domain;
using Xunit;

namespace KArtSell.ModelOperations.UnitTests;

public class VS02_SecurityMasterPolicyTests
{
    [Fact]
    public void ResolveSyncConflict_LocalVersionAhead_ReturnsIdempotent()
    {
        var state = new SyncState(
            LocalVersion: 5,
            RemoteVersion: 3,
            LocalRules: new(),
            RemoteRules: new(),
            IdempotencyKey: "key-123",
            CorrelationId: "corr-456");

        var result = SecurityMasterPolicy.ResolveSyncConflict(state);

        Assert.True(result.IsSuccess);
        Assert.Equal(5, result.NewVersion);
        Assert.Empty(result.AppliedRules);
    }

    [Fact]
    public void ResolveSyncConflict_VersionsMatch_ReturnsIdempotent()
    {
        var state = new SyncState(
            LocalVersion: 5,
            RemoteVersion: 5,
            LocalRules: new(),
            RemoteRules: new(),
            IdempotencyKey: "key-123",
            CorrelationId: "corr-456");

        var result = SecurityMasterPolicy.ResolveSyncConflict(state);

        Assert.True(result.IsSuccess);
        Assert.Equal(5, result.NewVersion);
    }

    [Fact]
    public void ResolveSyncConflict_RemoteAhead_AppliesNewRules()
    {
        var remoteRule = new SecurityRule(
            RuleId: Guid.NewGuid(),
            ResourceName: "api/users",
            Action: "read",
            Version: 1,
            EffectiveAt: DateTime.UtcNow,
            ExpiresAt: null,
            PublishedAt: DateTime.UtcNow,
            CorrelationId: "corr-456");

        var state = new SyncState(
            LocalVersion: 1,
            RemoteVersion: 2,
            LocalRules: new(),
            RemoteRules: new() { remoteRule },
            IdempotencyKey: "key-123",
            CorrelationId: "corr-456");

        var result = SecurityMasterPolicy.ResolveSyncConflict(state);

        Assert.True(result.IsSuccess);
        Assert.Equal(2, result.NewVersion);
        Assert.Single(result.AppliedRules);
        Assert.Equal(remoteRule.RuleId, result.AppliedRules[0].RuleId);
    }

    [Fact]
    public void ResolveSyncConflict_LastWriteWins_UsesNewerTimestamp()
    {
        var ruleId = Guid.NewGuid();
        var olderTime = DateTime.UtcNow.AddMinutes(-5);
        var newerTime = DateTime.UtcNow;

        var localRule = new SecurityRule(
            RuleId: ruleId,
            ResourceName: "api/users",
            Action: "read",
            Version: 1,
            EffectiveAt: DateTime.UtcNow,
            ExpiresAt: null,
            PublishedAt: olderTime,
            CorrelationId: "corr-456");

        var remoteRule = new SecurityRule(
            RuleId: ruleId,
            ResourceName: "api/users",
            Action: "write",
            Version: 2,
            EffectiveAt: DateTime.UtcNow,
            ExpiresAt: null,
            PublishedAt: newerTime,
            CorrelationId: "corr-456");

        var state = new SyncState(
            LocalVersion: 1,
            RemoteVersion: 2,
            LocalRules: new() { localRule },
            RemoteRules: new() { remoteRule },
            IdempotencyKey: "key-123",
            CorrelationId: "corr-456");

        var result = SecurityMasterPolicy.ResolveSyncConflict(state);

        Assert.True(result.IsSuccess);
        Assert.Single(result.AppliedRules);
        Assert.Equal("write", result.AppliedRules[0].Action);
    }

    [Fact]
    public void ValidateRule_ValidRule_ReturnsTrue()
    {
        var rule = new SecurityRule(
            RuleId: Guid.NewGuid(),
            ResourceName: "api/users",
            Action: "read",
            Version: 1,
            EffectiveAt: DateTime.UtcNow,
            ExpiresAt: DateTime.UtcNow.AddDays(30),
            PublishedAt: DateTime.UtcNow,
            CorrelationId: "corr-456");

        var (isValid, errors) = SecurityMasterPolicy.ValidateRule(rule);

        Assert.True(isValid);
        Assert.Empty(errors);
    }

    [Fact]
    public void ValidateRule_InvalidAction_ReturnsFalse()
    {
        var rule = new SecurityRule(
            RuleId: Guid.NewGuid(),
            ResourceName: "api/users",
            Action: "DELETE",
            Version: 1,
            EffectiveAt: DateTime.UtcNow,
            ExpiresAt: null,
            PublishedAt: DateTime.UtcNow,
            CorrelationId: "corr-456");

        var (isValid, errors) = SecurityMasterPolicy.ValidateRule(rule);

        Assert.False(isValid);
        Assert.Contains("Action must be one of", errors[0]);
    }

    [Fact]
    public void ValidateRule_ExpiresBeforeEffective_ReturnsFalse()
    {
        var rule = new SecurityRule(
            RuleId: Guid.NewGuid(),
            ResourceName: "api/users",
            Action: "read",
            Version: 1,
            EffectiveAt: DateTime.UtcNow.AddDays(10),
            ExpiresAt: DateTime.UtcNow.AddDays(5),
            PublishedAt: DateTime.UtcNow,
            CorrelationId: "corr-456");

        var (isValid, errors) = SecurityMasterPolicy.ValidateRule(rule);

        Assert.False(isValid);
        Assert.Contains("EffectiveAt must be before", errors[0]);
    }

    [Fact]
    public void IsRuleActive_BeforeEffectiveTime_ReturnsFalse()
    {
        var rule = new SecurityRule(
            RuleId: Guid.NewGuid(),
            ResourceName: "api/users",
            Action: "read",
            Version: 1,
            EffectiveAt: DateTime.UtcNow.AddDays(1),
            ExpiresAt: null,
            PublishedAt: DateTime.UtcNow,
            CorrelationId: "corr-456");

        var isActive = SecurityMasterPolicy.IsRuleActive(rule, DateTime.UtcNow);

        Assert.False(isActive);
    }

    [Fact]
    public void IsRuleActive_AfterExpiryTime_ReturnsFalse()
    {
        var rule = new SecurityRule(
            RuleId: Guid.NewGuid(),
            ResourceName: "api/users",
            Action: "read",
            Version: 1,
            EffectiveAt: DateTime.UtcNow.AddDays(-1),
            ExpiresAt: DateTime.UtcNow.AddMinutes(-1),
            PublishedAt: DateTime.UtcNow.AddDays(-1),
            CorrelationId: "corr-456");

        var isActive = SecurityMasterPolicy.IsRuleActive(rule, DateTime.UtcNow);

        Assert.False(isActive);
    }

    [Fact]
    public void IsRuleActive_WithinWindow_ReturnsTrue()
    {
        var now = DateTime.UtcNow;
        var rule = new SecurityRule(
            RuleId: Guid.NewGuid(),
            ResourceName: "api/users",
            Action: "read",
            Version: 1,
            EffectiveAt: now.AddHours(-1),
            ExpiresAt: now.AddHours(1),
            PublishedAt: now.AddDays(-1),
            CorrelationId: "corr-456");

        var isActive = SecurityMasterPolicy.IsRuleActive(rule, now);

        Assert.True(isActive);
    }

    [Fact]
    public void CreateIdempotencyKey_FormatsCorrectly()
    {
        var key = SecurityMasterPolicy.CreateIdempotencyKey(5, "corr-123");

        Assert.Equal("sync-5-corr-123", key);
    }

    [Fact]
    public void RequiresRollback_NoRulesAppliedButVersionIncremented_ReturnsTrue()
    {
        var requiresRollback = SecurityMasterPolicy.RequiresRollback(0, 1);

        Assert.True(requiresRollback);
    }

    [Fact]
    public void RequiresRollback_RulesApplied_ReturnsFalse()
    {
        var requiresRollback = SecurityMasterPolicy.RequiresRollback(5, 1);

        Assert.False(requiresRollback);
    }
}
