using System.Net;

namespace ZB.MOM.WW.ScadaBridge.ExternalSystemGateway.Tests;

/// <summary>
/// WP-8: Tests for HTTP error classification.
/// Transient: connection refused, timeout, HTTP 408/429/5xx.
/// Permanent: HTTP 4xx (except 408/429).
/// </summary>
public class ErrorClassifierTests
{
    [Theory]
    [InlineData(HttpStatusCode.InternalServerError, true)]
    [InlineData(HttpStatusCode.BadGateway, true)]
    [InlineData(HttpStatusCode.ServiceUnavailable, true)]
    [InlineData(HttpStatusCode.GatewayTimeout, true)]
    [InlineData(HttpStatusCode.RequestTimeout, true)]
    [InlineData((HttpStatusCode)429, true)] // TooManyRequests
    public void TransientStatusCodes_ClassifiedCorrectly(HttpStatusCode statusCode, bool expectedTransient)
    {
        Assert.Equal(expectedTransient, ErrorClassifier.IsTransient(statusCode));
    }

    [Theory]
    [InlineData(HttpStatusCode.BadRequest, false)]
    [InlineData(HttpStatusCode.Unauthorized, false)]
    [InlineData(HttpStatusCode.Forbidden, false)]
    [InlineData(HttpStatusCode.NotFound, false)]
    [InlineData(HttpStatusCode.MethodNotAllowed, false)]
    [InlineData(HttpStatusCode.Conflict, false)]
    public void PermanentStatusCodes_ClassifiedCorrectly(HttpStatusCode statusCode, bool expectedTransient)
    {
        Assert.Equal(expectedTransient, ErrorClassifier.IsTransient(statusCode));
    }

    [Fact]
    public void HttpRequestException_IsTransient()
    {
        Assert.True(ErrorClassifier.IsTransient(new HttpRequestException("Connection refused")));
    }

    [Fact]
    public void TaskCanceledException_IsTransient()
    {
        Assert.True(ErrorClassifier.IsTransient(new TaskCanceledException("Timeout")));
    }

    [Fact]
    public void TimeoutException_IsTransient()
    {
        Assert.True(ErrorClassifier.IsTransient(new TimeoutException()));
    }

    [Fact]
    public void GenericException_IsNotTransient()
    {
        Assert.False(ErrorClassifier.IsTransient(new InvalidOperationException("bad input")));
    }

    [Fact]
    public void AsTransient_CreatesCorrectException()
    {
        var ex = ErrorClassifier.AsTransient("test message");
        Assert.IsType<TransientExternalSystemException>(ex);
        Assert.Equal("test message", ex.Message);
    }

    [Fact]
    public void IsTransient_CallerCancelledOce_IsNotTransient()
    {
        var cts = new CancellationTokenSource();
        cts.Cancel();
        Assert.False(ErrorClassifier.IsTransient(new OperationCanceledException(), cts.Token));
        Assert.True(ErrorClassifier.IsTransient(new OperationCanceledException(), CancellationToken.None));
    }
}
