using System.Net;
using System.Text.Json;
using ApiOPE.Contracts;

namespace ApiOPE.Services;

public enum InternalLookupStatus
{
    Success,
    NotFound
}

public sealed record InternalLookupResult(
    InternalLookupStatus Status,
    InternalGestionaFieldsResponse? Fields);

public sealed class InternalApiException : Exception
{
    public InternalApiException(string message)
        : base(message)
    {
    }

    public InternalApiException(string message, Exception innerException)
        : base(message, innerException)
    {
    }
}

public sealed class InternalDenunciasClient
{
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
    {
        PropertyNameCaseInsensitive = false,
        MaxDepth = 16
    };

    private readonly HttpClient _httpClient;
    private readonly ILogger<InternalDenunciasClient> _logger;

    public InternalDenunciasClient(
        HttpClient httpClient,
        ILogger<InternalDenunciasClient> logger)
    {
        _httpClient = httpClient;
        _logger = logger;
    }

    public async Task<InternalLookupResult> GetFieldsAsync(
        DenunciaLookup lookup,
        Guid transactionId,
        CancellationToken cancellationToken)
    {
        using var request = new HttpRequestMessage(HttpMethod.Get, lookup.RelativePath);
        using var response = await _httpClient.SendAsync(
            request,
            HttpCompletionOption.ResponseHeadersRead,
            cancellationToken);

        if (response.StatusCode is HttpStatusCode.NotFound or HttpStatusCode.Gone)
        {
            return new InternalLookupResult(InternalLookupStatus.NotFound, null);
        }

        if (!response.IsSuccessStatusCode)
        {
            _logger.LogWarning(
                "ApiDenuncias ha rechazado una consulta OPE. Status={StatusCode}; TransactionId={TransactionId}",
                (int)response.StatusCode,
                transactionId);
            throw new InternalApiException("ApiDenuncias no ha podido completar la consulta OPE.");
        }

        try
        {
            await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
            var fields = await JsonSerializer.DeserializeAsync<InternalGestionaFieldsResponse>(
                stream,
                JsonOptions,
                cancellationToken);

            if (fields?.Data is null)
            {
                throw new InvalidDataException("La respuesta interna no contiene el objeto data.");
            }

            return new InternalLookupResult(InternalLookupStatus.Success, fields);
        }
        catch (JsonException exception)
        {
            throw new InternalApiException("ApiDenuncias ha devuelto una respuesta no valida.", exception);
        }
        catch (InvalidDataException exception)
        {
            throw new InternalApiException("ApiDenuncias ha devuelto una respuesta incompleta.", exception);
        }
    }
}
