using Dapper;
using KArtSell.Host.Infrastructure;
using Npgsql;
using Xunit;

namespace KArtSell.Integration.Tests;

[Collection("Database")]
public class CircuitBreakerTests : IAsyncLifetime
{
    private readonly NpgsqlDataSource _dataSource;
    private readonly CircuitBreakerPolicyFactory _factory;

    public CircuitBreakerTests(DatabaseFixture fixture)
    {
        _dataSource = fixture.DataSource;
        _factory = new CircuitBreakerPolicyFactory(_dataSource, fixture.Clock(), fixture.Logger<CircuitBreakerPolicyFactory>());
    }

    public async Task InitializeAsync()
    {
        await using var conn = await _dataSource.OpenConnectionAsync();
        await conn.ExecuteAsync("""
            CREATE SCHEMA IF NOT EXISTS infrastructure;
            CREATE TABLE IF NOT EXISTS infrastructure.circuit_breaker_state (
                id BIGSERIAL PRIMARY KEY,
                api_name VARCHAR(50) NOT NULL UNIQUE,
                state VARCHAR(50) NOT NULL,
                failure_count INT NOT NULL,
                last_failure_at TIMESTAMP WITH TIME ZONE,
                opened_at TIMESTAMP WITH TIME ZONE,
                updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
                published_at TIMESTAMP WITH TIME ZONE NOT NULL
            );
            CREATE TABLE IF NOT EXISTS infrastructure.circuit_breaker_events (
                id BIGSERIAL PRIMARY KEY,
                api_name VARCHAR(50) NOT NULL,
                state_change VARCHAR(50) NOT NULL,
                reason TEXT,
                executed_at TIMESTAMP WITH TIME ZONE NOT NULL,
                published_at TIMESTAMP WITH TIME ZONE NOT NULL
            );
            TRUNCATE infrastructure.circuit_breaker_state CASCADE;
            TRUNCATE infrastructure.circuit_breaker_events CASCADE;
            """);
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public void GetPolicy_ReturnsPolicy_ForValidApi()
    {
        // Act
        var policy = _factory.GetPolicy("krx");

        // Assert
        Assert.NotNull(policy);
    }

    [Fact]
    public void GetPolicy_CachesPolicy_OnSecondCall()
    {
        // Act
        var policy1 = _factory.GetPolicy("krx");
        var policy2 = _factory.GetPolicy("krx");

        // Assert - Same instance (cached)
        Assert.Same(policy1, policy2);
    }

    [Fact]
    public void Classify_ReturnsTransient_For429TooManyRequests()
    {
        // Act
        var classification = CircuitBreakerPolicyFactory.Classify(
            new HttpRequestException(),
            System.Net.HttpStatusCode.TooManyRequests);

        // Assert
        Assert.Equal(FailureClassification.Transient, classification);
    }

    [Fact]
    public void Classify_ReturnsPermanent_For400BadRequest()
    {
        // Act
        var classification = CircuitBreakerPolicyFactory.Classify(
            new HttpRequestException(),
            System.Net.HttpStatusCode.BadRequest);

        // Assert
        Assert.Equal(FailureClassification.Permanent, classification);
    }

    [Fact]
    public void Classify_ReturnsDataQuality_ForUnknownException()
    {
        // Act
        var classification = CircuitBreakerPolicyFactory.Classify(
            new InvalidOperationException("Unknown error"),
            null);

        // Assert
        Assert.Equal(FailureClassification.DataQuality, classification);
    }
}
