using System.Net;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using ScadaLink.Commons.Entities.ExternalSystems;
using ScadaLink.Commons.Interfaces.Repositories;

namespace ScadaLink.ExternalSystemGateway.Tests;

/// <summary>
/// WP-6/7: Tests for ExternalSystemClient — HTTP client, call modes, error handling.
/// </summary>
public class ExternalSystemClientTests
{
    private readonly IExternalSystemRepository _repository = Substitute.For<IExternalSystemRepository>();
    private readonly IHttpClientFactory _httpClientFactory = Substitute.For<IHttpClientFactory>();

    [Fact]
    public async Task Call_SystemNotFound_ReturnsError()
    {
        _repository.GetAllExternalSystemsAsync().Returns(new List<ExternalSystemDefinition>());

        var client = new ExternalSystemClient(
            _httpClientFactory, _repository,
            NullLogger<ExternalSystemClient>.Instance);

        var result = await client.CallAsync("nonexistent", "method");

        Assert.False(result.Success);
        Assert.Contains("not found", result.ErrorMessage);
    }

    [Fact]
    public async Task Call_MethodNotFound_ReturnsError()
    {
        var system = new ExternalSystemDefinition("TestAPI", "https://api.example.com", "none") { Id = 1 };
        _repository.GetAllExternalSystemsAsync().Returns(new List<ExternalSystemDefinition> { system });
        _repository.GetMethodsByExternalSystemIdAsync(1).Returns(new List<ExternalSystemMethod>());

        var client = new ExternalSystemClient(
            _httpClientFactory, _repository,
            NullLogger<ExternalSystemClient>.Instance);

        var result = await client.CallAsync("TestAPI", "missingMethod");

        Assert.False(result.Success);
        Assert.Contains("not found", result.ErrorMessage);
    }

    [Fact]
    public async Task Call_SuccessfulHttp_ReturnsResponse()
    {
        var system = new ExternalSystemDefinition("TestAPI", "https://api.example.com", "none") { Id = 1 };
        var method = new ExternalSystemMethod("getData", "GET", "/data") { Id = 1, ExternalSystemDefinitionId = 1 };

        _repository.GetAllExternalSystemsAsync().Returns(new List<ExternalSystemDefinition> { system });
        _repository.GetMethodsByExternalSystemIdAsync(1).Returns(new List<ExternalSystemMethod> { method });

        var handler = new MockHttpMessageHandler(HttpStatusCode.OK, "{\"result\": 42}");
        var httpClient = new HttpClient(handler);
        _httpClientFactory.CreateClient(Arg.Any<string>()).Returns(httpClient);

        var client = new ExternalSystemClient(
            _httpClientFactory, _repository,
            NullLogger<ExternalSystemClient>.Instance);

        var result = await client.CallAsync("TestAPI", "getData");

        Assert.True(result.Success);
        Assert.Contains("42", result.ResponseJson!);
    }

    [Fact]
    public async Task Call_Transient500_ReturnsTransientError()
    {
        var system = new ExternalSystemDefinition("TestAPI", "https://api.example.com", "none") { Id = 1 };
        var method = new ExternalSystemMethod("failMethod", "POST", "/fail") { Id = 1, ExternalSystemDefinitionId = 1 };

        _repository.GetAllExternalSystemsAsync().Returns(new List<ExternalSystemDefinition> { system });
        _repository.GetMethodsByExternalSystemIdAsync(1).Returns(new List<ExternalSystemMethod> { method });

        var handler = new MockHttpMessageHandler(HttpStatusCode.InternalServerError, "server error");
        var httpClient = new HttpClient(handler);
        _httpClientFactory.CreateClient(Arg.Any<string>()).Returns(httpClient);

        var client = new ExternalSystemClient(
            _httpClientFactory, _repository,
            NullLogger<ExternalSystemClient>.Instance);

        var result = await client.CallAsync("TestAPI", "failMethod");

        Assert.False(result.Success);
        Assert.Contains("Transient error", result.ErrorMessage);
    }

    [Fact]
    public async Task Call_Permanent400_ReturnsPermanentError()
    {
        var system = new ExternalSystemDefinition("TestAPI", "https://api.example.com", "none") { Id = 1 };
        var method = new ExternalSystemMethod("badMethod", "POST", "/bad") { Id = 1, ExternalSystemDefinitionId = 1 };

        _repository.GetAllExternalSystemsAsync().Returns(new List<ExternalSystemDefinition> { system });
        _repository.GetMethodsByExternalSystemIdAsync(1).Returns(new List<ExternalSystemMethod> { method });

        var handler = new MockHttpMessageHandler(HttpStatusCode.BadRequest, "bad request");
        var httpClient = new HttpClient(handler);
        _httpClientFactory.CreateClient(Arg.Any<string>()).Returns(httpClient);

        var client = new ExternalSystemClient(
            _httpClientFactory, _repository,
            NullLogger<ExternalSystemClient>.Instance);

        var result = await client.CallAsync("TestAPI", "badMethod");

        Assert.False(result.Success);
        Assert.Contains("Permanent error", result.ErrorMessage);
    }

    [Fact]
    public async Task CachedCall_SystemNotFound_ReturnsError()
    {
        _repository.GetAllExternalSystemsAsync().Returns(new List<ExternalSystemDefinition>());

        var client = new ExternalSystemClient(
            _httpClientFactory, _repository,
            NullLogger<ExternalSystemClient>.Instance);

        var result = await client.CachedCallAsync("nonexistent", "method");

        Assert.False(result.Success);
        Assert.Contains("not found", result.ErrorMessage);
    }

    [Fact]
    public async Task CachedCall_Success_ReturnsDirectly()
    {
        var system = new ExternalSystemDefinition("TestAPI", "https://api.example.com", "none") { Id = 1 };
        var method = new ExternalSystemMethod("getData", "GET", "/data") { Id = 1, ExternalSystemDefinitionId = 1 };

        _repository.GetAllExternalSystemsAsync().Returns(new List<ExternalSystemDefinition> { system });
        _repository.GetMethodsByExternalSystemIdAsync(1).Returns(new List<ExternalSystemMethod> { method });

        var handler = new MockHttpMessageHandler(HttpStatusCode.OK, "{\"ok\": true}");
        var httpClient = new HttpClient(handler);
        _httpClientFactory.CreateClient(Arg.Any<string>()).Returns(httpClient);

        var client = new ExternalSystemClient(
            _httpClientFactory, _repository,
            NullLogger<ExternalSystemClient>.Instance);

        var result = await client.CachedCallAsync("TestAPI", "getData");

        Assert.True(result.Success);
        Assert.False(result.WasBuffered);
    }

    /// <summary>
    /// Test helper: mock HTTP message handler.
    /// </summary>
    private class MockHttpMessageHandler : HttpMessageHandler
    {
        private readonly HttpStatusCode _statusCode;
        private readonly string _body;

        public MockHttpMessageHandler(HttpStatusCode statusCode, string body)
        {
            _statusCode = statusCode;
            _body = body;
        }

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return Task.FromResult(new HttpResponseMessage(_statusCode)
            {
                Content = new StringContent(_body)
            });
        }
    }
}
