using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Time.Testing;
using Xunit;

namespace StellaOps.Policy.Tests;

public sealed class PolicySnapshotStoreTests
{
    private const string BasePolicyYaml = """
version: "1.0"
rules:
  - name: Block Critical
    severity: [Critical]
    action: block
""";

    [Fact]
    public async Task SaveAsync_CreatesNewSnapshotAndAuditEntry()
    {
        var snapshotRepo = new InMemoryPolicySnapshotRepository();
        var auditRepo = new InMemoryPolicyAuditRepository();
        var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 10, 18, 10, 0, 0, TimeSpan.Zero));
        var store = new PolicySnapshotStore(snapshotRepo, auditRepo, timeProvider, NullLogger<PolicySnapshotStore>.Instance);

        var content = new PolicySnapshotContent(BasePolicyYaml, PolicyDocumentFormat.Yaml, "cli", "test", null);

        var result = await store.SaveAsync(content, CancellationToken.None);

        Assert.True(result.Success);
        Assert.True(result.Created);
        Assert.NotNull(result.Snapshot);
        Assert.Equal("rev-1", result.Snapshot!.RevisionId);
        Assert.Equal(result.Digest, result.Snapshot.Digest);
        Assert.Equal(timeProvider.GetUtcNow(), result.Snapshot.CreatedAt);
        Assert.Equal(PolicyScoringConfig.Default.Version, result.Snapshot.ScoringConfig.Version);

        var latest = await store.GetLatestAsync();
        Assert.Equal(result.Snapshot, latest);

        var audits = await auditRepo.ListAsync(10);
        Assert.Single(audits);
        Assert.Equal(result.Digest, audits[0].Digest);
        Assert.Equal("snapshot.created", audits[0].Action);
        Assert.Equal("rev-1", audits[0].RevisionId);
    }

    [Fact]
    public async Task SaveAsync_DoesNotCreateNewRevisionWhenDigestUnchanged()
    {
        var snapshotRepo = new InMemoryPolicySnapshotRepository();
        var auditRepo = new InMemoryPolicyAuditRepository();
        var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 10, 18, 10, 0, 0, TimeSpan.Zero));
        var store = new PolicySnapshotStore(snapshotRepo, auditRepo, timeProvider, NullLogger<PolicySnapshotStore>.Instance);

        var content = new PolicySnapshotContent(BasePolicyYaml, PolicyDocumentFormat.Yaml, "cli", "test", null);
        var first = await store.SaveAsync(content, CancellationToken.None);
        Assert.True(first.Created);

        timeProvider.Advance(TimeSpan.FromHours(1));
        var second = await store.SaveAsync(content, CancellationToken.None);

        Assert.True(second.Success);
        Assert.False(second.Created);
        Assert.Equal(first.Digest, second.Digest);
        Assert.Equal("rev-1", second.Snapshot!.RevisionId);
        Assert.Equal(PolicyScoringConfig.Default.Version, second.Snapshot.ScoringConfig.Version);

        var audits = await auditRepo.ListAsync(10);
        Assert.Single(audits);
    }

    [Fact]
    public async Task SaveAsync_ReturnsFailureWhenValidationFails()
    {
        var snapshotRepo = new InMemoryPolicySnapshotRepository();
        var auditRepo = new InMemoryPolicyAuditRepository();
        var store = new PolicySnapshotStore(snapshotRepo, auditRepo, TimeProvider.System, NullLogger<PolicySnapshotStore>.Instance);

        const string invalidYaml = "version: '1.0'\nrules: []";
        var content = new PolicySnapshotContent(invalidYaml, PolicyDocumentFormat.Yaml, null, null, null);

        var result = await store.SaveAsync(content, CancellationToken.None);

        Assert.False(result.Success);
        Assert.False(result.Created);
        Assert.Null(result.Snapshot);

        var audits = await auditRepo.ListAsync(5);
        Assert.Empty(audits);
    }
}
