using Dapper;
using KArtSell.BuildingBlocks.Observability;
using KArtSell.Host.Features.Observability;
using Npgsql;
using Xunit;

namespace KArtSell.Integration.Tests;

[Collection("Database")]
public class ObservabilityMetricsTests : IAsyncLifetime
{
    private readonly NpgsqlDataSource _dataSource;
    private readonly MetricsPolicy _policy;
    private readonly MetricsSql _sql;

    public ObservabilityMetricsTests(DatabaseFixture fixture)
    {
        _dataSource = fixture.DataSource;
        _policy = new MetricsPolicy(fixture.Clock());
        _sql = new MetricsSql(_dataSource, fixture.Clock());
    }

    public async Task InitializeAsync()
    {
        await using var conn = await _dataSource.OpenConnectionAsync();
        await conn.ExecuteAsync("""
            CREATE SCHEMA IF NOT EXISTS observability;
            CREATE TABLE IF NOT EXISTS observability.batch_sla_metrics (
                id BIGSERIAL PRIMARY KEY,
                job_name VARCHAR(100) NOT NULL,
                job_type VARCHAR(50) NOT NULL,
                started_at TIMESTAMP WITH TIME ZONE NOT NULL,
                completed_at TIMESTAMP WITH TIME ZONE NOT NULL,
                duration_seconds INT NOT NULL,
                status VARCHAR(50) NOT NULL,
                recorded_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
                published_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
            );
            CREATE TABLE IF NOT EXISTS observability.data_quality_quarantine (
                id BIGSERIAL PRIMARY KEY,
                module_name VARCHAR(100) NOT NULL,
                reason VARCHAR(256) NOT NULL,
                entity_id UUID,
                entity_type VARCHAR(50),
                quarantined_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
                resolution_status VARCHAR(50),
                published_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
            );
            TRUNCATE observability.batch_sla_metrics CASCADE;
            TRUNCATE observability.data_quality_quarantine CASCADE;
            """);
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public void BuildMetricsResponse_ReturnsValidSchema()
    {
        // Arrange
        var batchSla = (Total: 10, OnTime: 8, AvgTime: new TimeSpan(0, 5, 30));
        var dataQuality = (Quarantined: 1, Total: 100, Errors: new List<string> { "timeout" });
        var duplicates = (Detected: 2, Resolved: 1, LastCheck: DateTime.UtcNow);
        var reconciliation = (Detected: 0, Resolved: 0, Pending: new List<string>());
        var modelDrift = (Baseline: 1.5m, Current: 1.0m);  // 33% drift (WARNING threshold is 15%, CRITICAL is 30%)

        // Act
        var response = _policy.BuildMetricsResponse(batchSla, dataQuality, duplicates, reconciliation, modelDrift);

        // Assert
        Assert.NotNull(response);
        Assert.Equal(80, response.BatchSla.SlaPercentage);
        Assert.Equal(99, response.DataQuality.QualityPercentage);
        Assert.Equal("CRITICAL", response.ModelDrift.Status);  // 33% drift
    }

    [Fact]
    public void BuildBatchSlaMetrics_CalculatesPercentageCorrectly()
    {
        // Arrange
        var batchSla = (Total: 100, OnTime: 95, AvgTime: new TimeSpan(0, 10, 0));

        // Act
        var response = _policy.BuildMetricsResponse(batchSla, null, null, null, null);

        // Assert
        Assert.Equal(95m, response.BatchSla.SlaPercentage);
    }

    [Fact]
    public void BuildModelDriftMetrics_ReturnsCritical_WhenDriftExceeds30Percent()
    {
        // Arrange
        var modelDrift = (Baseline: 1.0m, Current: 0.5m);  // 50% loss

        // Act
        var response = _policy.BuildMetricsResponse(null, null, null, null, modelDrift);

        // Assert
        Assert.Equal("CRITICAL", response.ModelDrift.Status);
    }

    [Fact]
    public async Task GetBatchSlaAsync_ReturnsNull_WhenNoData()
    {
        // Act
        var result = await _sql.GetBatchSlaAsync();

        // Assert
        Assert.Null(result);
    }

    [Fact]
    public async Task GetDataQualityQuarantineAsync_ReturnsNull_WhenNoData()
    {
        // Act
        var result = await _sql.GetDataQualityQuarantineAsync();

        // Assert
        Assert.Null(result);
    }
}
