using System.Data;
using Dapper;
using Microsoft.Extensions.Logging;
using Npgsql;
using Xunit;
using KArtSell.Modules.ModelOperations.Compliance;

namespace KArtSell.Integration.Tests.Compliance;

public class AuditTrailTests : IAsyncLifetime
{
    private readonly IDbConnection _db;
    private readonly AuditSql _sql;

    public AuditTrailTests()
    {
        _db = new NpgsqlConnection(TestConnectionString);
        _sql = new AuditSql(LoggerFactory.Create(b => b.AddConsole()).CreateLogger<AuditSql>());
    }

    public async Task InitializeAsync()
    {
        _db.Open();
        await _db.ExecuteAsync(@"
            DELETE FROM compliance.gdpr_retention;
            DELETE FROM compliance.audit_events;
        ");
    }

    public Task DisposeAsync()
    {
        _db?.Dispose();
        return Task.CompletedTask;
    }

    [Fact]
    public async Task InsertAuditEvent_CreatesImmutableRecord()
    {
        // Arrange
        var eventId = Guid.NewGuid();
        var correlationId = Guid.NewGuid();
        var entityId = Guid.NewGuid();

        // Act
        await _sql.InsertAuditEventAsync(
            _db,
            eventId,
            AuditEventTypes.ModelActivated,
            AuditEntityTypes.Model,
            entityId,
            "sre@company.com",
            "SRE",
            DateTime.UtcNow,
            "SUCCESS",
            null,
            new Dictionary<string, object> { { "modelVersion", "1.0.0" } },
            new[] { "s3://evidence/pbo-0.95.json" },
            "192.168.1.100",
            "PostmanRuntime/7.32.3",
            correlationId,
            CancellationToken.None);

        // Assert
        var @event = await _sql.GetAuditEventByIdAsync(_db, eventId, CancellationToken.None);
        Assert.NotNull(@event);
        Assert.Equal(AuditEventTypes.ModelActivated, @event.EventType);
        Assert.Equal(entityId, @event.EntityId);
        Assert.Equal("sre@company.com", @event.ActorEmail);
        Assert.Single(@event.EvidenceLinks!);
    }

    [Fact]
    public async Task QueryAuditEvents_WithFilters_ReturnsMatching()
    {
        // Arrange
        var entityId = Guid.NewGuid();
        var correlationId = Guid.NewGuid();
        await _sql.InsertAuditEventAsync(
            _db, Guid.NewGuid(), AuditEventTypes.ModelActivated, AuditEntityTypes.Model,
            entityId, "sre@company.com", "SRE", DateTime.UtcNow, "SUCCESS",
            null, null, null, null, null, correlationId, CancellationToken.None);

        await _sql.InsertAuditEventAsync(
            _db, Guid.NewGuid(), AuditEventTypes.ApprovalApproved, AuditEntityTypes.Approval,
            Guid.NewGuid(), "checker@company.com", "CHECKER", DateTime.UtcNow, "SUCCESS",
            null, null, null, null, null, Guid.NewGuid(), CancellationToken.None);

        // Act
        var (events, total) = await _sql.QueryAuditEventsAsync(
            _db,
            eventType: AuditEventTypes.ModelActivated,
            take: 50,
            ct: CancellationToken.None);

        // Assert
        Assert.Equal(1, total);
        Assert.Single(events);
        Assert.Equal(AuditEventTypes.ModelActivated, events[0].EventType);
    }

    [Fact]
    public async Task InsertGdprRetention_TracksPersonalData()
    {
        // Arrange
        var eventId = Guid.NewGuid();
        var customerId = Guid.NewGuid();
        var retentionId = Guid.NewGuid();

        await _sql.InsertAuditEventAsync(
            _db, eventId, AuditEventTypes.ModelActivated, AuditEntityTypes.Model,
            Guid.NewGuid(), "customer@company.com", null, DateTime.UtcNow, "SUCCESS", null,
            null, null, null, null, Guid.NewGuid(), CancellationToken.None);

        // Act
        await _sql.InsertGdprRetentionAsync(
            _db, retentionId, eventId, customerId,
            new[] { GdprDataCategories.PersonallyIdentifiableInformation, GdprDataCategories.EmailAddress },
            DateTime.UtcNow.AddYears(7),
            CancellationToken.None);

        // Assert
        var retention = await _db.QuerySingleAsync<GdprRetention>(
            "SELECT * FROM compliance.gdpr_retention WHERE id = @Id",
            new { Id = retentionId });
        Assert.NotNull(retention);
        Assert.Equal(customerId, retention.CustomerId);
        Assert.Equal(GdprPurgeStatus.Pending, retention.PurgeStatus);
    }

    [Fact]
    public async Task MarkGdprPurged_RedactsPersonalData()
    {
        // Arrange
        var customerId = Guid.NewGuid();
        var eventId = Guid.NewGuid();
        var retentionId = Guid.NewGuid();

        await _sql.InsertAuditEventAsync(
            _db, eventId, AuditEventTypes.ModelActivated, AuditEntityTypes.Model,
            Guid.NewGuid(), "customer@company.com", null, DateTime.UtcNow, "SUCCESS", null,
            new Dictionary<string, object> { { "customer_id", customerId.ToString() } },
            null, null, null, Guid.NewGuid(), CancellationToken.None);

        await _sql.InsertGdprRetentionAsync(
            _db, retentionId, eventId, customerId,
            new[] { GdprDataCategories.PersonallyIdentifiableInformation },
            DateTime.UtcNow.AddYears(7),
            CancellationToken.None);

        // Act
        await _sql.MarkGdprPurgedAsync(_db, customerId, CancellationToken.None);

        // Assert
        var retention = await _db.QuerySingleAsync<GdprRetention>(
            "SELECT * FROM compliance.gdpr_retention WHERE id = @Id",
            new { Id = retentionId });
        Assert.Equal(GdprPurgeStatus.Purged, retention.PurgeStatus);
        Assert.NotNull(retention.PurgedAt);
    }

    [Fact]
    public async Task RedactAuditEventDetails_AnonymizesPersonalInfo()
    {
        // Arrange
        var eventId = Guid.NewGuid();
        var customerId = Guid.NewGuid();
        await _sql.InsertAuditEventAsync(
            _db, eventId, AuditEventTypes.ModelActivated, AuditEntityTypes.Model,
            Guid.NewGuid(), "customer@company.com", null, DateTime.UtcNow, "SUCCESS", null,
            new Dictionary<string, object>
            {
                { "actor_email", "customer@company.com" },
                { "customer_id", customerId.ToString() }
            },
            null, null, null, Guid.NewGuid(), CancellationToken.None);

        // Act
        await _sql.RedactAuditEventDetailsAsync(_db, eventId, CancellationToken.None);

        // Assert
        var @event = await _sql.GetAuditEventByIdAsync(_db, eventId, CancellationToken.None);
        Assert.NotNull(@event);
        Assert.Equal("<redacted>", @event.Details?["actor_email"].ToString());
        Assert.Equal("<purged>", @event.Details?["customer_id"].ToString());
    }

    private const string TestConnectionString =
        "Host=localhost;Port=5432;Database=kartselldb;Username=kartsell;Password=kartsell4321@!";
}
