using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace Nalu.Web.Enrichers;

public class ViaCepEnricher : IEnricher
{
    private readonly HttpClient _http;
    private readonly ILogger<ViaCepEnricher> _logger;

    public string Name => "viacep";

    public ViaCepEnricher(HttpClient http, ILogger<ViaCepEnricher> logger)
    {
        _http = http;
        _logger = logger;
    }

    public async Task<EnrichmentResult> EnrichAsync(string value, CancellationToken ct = default)
    {
        var cep = Regex.Replace(value, @"\D", "");
        if (cep.Length != 8) return EnrichmentResult.NotFound();

        // Primary: ViaCEP
        try
        {
            var json = await _http.GetFromJsonAsync<JsonObject>(
                $"https://viacep.com.br/ws/{cep}/json/", ct);

            // ViaCEP returns {"erro": "true"} (string!) when not found
            var erroNode = json?["erro"];
            bool hasError = erroNode is not null &&
                (erroNode.GetValue<string?>() == "true" || (erroNode.GetValueKind() == System.Text.Json.JsonValueKind.True));

            if (!hasError && json != null)
                return BuildResult(cep, json["logradouro"]?.GetValue<string?>(),
                    json["bairro"]?.GetValue<string?>(),
                    json["localidade"]?.GetValue<string?>(),
                    json["uf"]?.GetValue<string?>());
        }
        catch (Exception ex) when (ex is not OperationCanceledException)
        {
            _logger.LogWarning(ex, "ViaCEP failed for CEP {Cep}, trying BrasilAPI", cep);
        }

        // Fallback: BrasilAPI
        try
        {
            var json = await _http.GetFromJsonAsync<JsonObject>(
                $"https://brasilapi.com.br/api/cep/v2/{cep}", ct);

            if (json != null)
                return BuildResult(cep,
                    json["street"]?.GetValue<string?>(),
                    json["neighborhood"]?.GetValue<string?>(),
                    json["city"]?.GetValue<string?>(),
                    json["state"]?.GetValue<string?>());
        }
        catch (Exception ex) when (ex is not OperationCanceledException)
        {
            _logger.LogWarning(ex, "BrasilAPI also failed for CEP {Cep}", cep);
        }

        return EnrichmentResult.NotFound();
    }

    private static EnrichmentResult BuildResult(string cep, string? logradouro, string? bairro, string? cidade, string? estado)
    {
        logradouro ??= "";
        bairro ??= "";
        cidade ??= "";
        estado ??= "";

        // Build a human-readable line that degrades gracefully when logradouro is empty
        // (neighbourhood/condominium CEPs from ViaCEP often have logradouro == "")
        var parts = new List<string>();
        if (!string.IsNullOrWhiteSpace(logradouro)) parts.Add(logradouro);
        if (!string.IsNullOrWhiteSpace(bairro))     parts.Add(bairro);
        var cityState = string.IsNullOrWhiteSpace(estado) ? cidade : $"{cidade}/{estado}";
        if (!string.IsNullOrWhiteSpace(cityState))  parts.Add(cityState);
        var formattedAddress = string.Join(", ", parts);

        var address = new
        {
            cep = $"{cep[..5]}-{cep[5..]}",
            logradouro,
            bairro,
            cidade,
            estado,
            formatted_address = formattedAddress
        };

        return EnrichmentResult.Ok(JsonSerializer.Serialize(address));
    }
}
