// ==============================================================================
// MORPH-SPEC - Asaas Client Template
// HTTP Client para integração com Asaas API
// ==============================================================================

using System.Net.Http.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace {{Namespace}}.Infrastructure.Services;

// ==============================================================================
// OPTIONS
// ==============================================================================

public class AsaasOptions
{
    public const string SectionName = "Asaas";

    /// <summary>
    /// Base URL da API (sandbox ou produção)
    /// Sandbox: https://sandbox.asaas.com/api/v3
    /// Produção: https://www.asaas.com/api/v3
    /// </summary>
    public string BaseUrl { get; set; } = "https://sandbox.asaas.com/api/v3";

    /// <summary>
    /// API Key do Asaas
    /// </summary>
    public string ApiKey { get; set; } = string.Empty;
}

// ==============================================================================
// INTERFACE
// ==============================================================================

public interface IAsaasClient
{
    // Customers
    Task<AsaasCustomer> CreateCustomerAsync(CreateCustomerRequest request, CancellationToken ct = default);
    Task<AsaasCustomer?> GetCustomerByIdAsync(string customerId, CancellationToken ct = default);
    Task<AsaasCustomer?> GetCustomerByCpfCnpjAsync(string cpfCnpj, CancellationToken ct = default);

    // Payments
    Task<AsaasPayment> CreatePaymentAsync(CreatePaymentRequest request, CancellationToken ct = default);
    Task<AsaasPayment?> GetPaymentAsync(string paymentId, CancellationToken ct = default);
    Task<AsaasPixQrCode?> GetPixQrCodeAsync(string paymentId, CancellationToken ct = default);

    // Subscriptions
    Task<AsaasSubscription> CreateSubscriptionAsync(CreateSubscriptionRequest request, CancellationToken ct = default);
    Task<AsaasSubscription?> GetSubscriptionAsync(string subscriptionId, CancellationToken ct = default);
    Task CancelSubscriptionAsync(string subscriptionId, CancellationToken ct = default);
}

// ==============================================================================
// IMPLEMENTATION
// ==============================================================================

public class AsaasClient : IAsaasClient
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<AsaasClient> _logger;

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

    // =========================================================================
    // CUSTOMERS
    // =========================================================================

    public async Task<AsaasCustomer> CreateCustomerAsync(CreateCustomerRequest request, CancellationToken ct = default)
    {
        _logger.LogInformation("Creating Asaas customer: {Name}", request.Name);

        var response = await _httpClient.PostAsJsonAsync("customers", request, ct);
        await EnsureSuccessAsync(response, "create customer");

        return await response.Content.ReadFromJsonAsync<AsaasCustomer>(ct)
            ?? throw new AsaasException("Failed to deserialize customer response");
    }

    public async Task<AsaasCustomer?> GetCustomerByIdAsync(string customerId, CancellationToken ct = default)
    {
        var response = await _httpClient.GetAsync($"customers/{customerId}", ct);

        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
            return null;

        await EnsureSuccessAsync(response, "get customer");
        return await response.Content.ReadFromJsonAsync<AsaasCustomer>(ct);
    }

    public async Task<AsaasCustomer?> GetCustomerByCpfCnpjAsync(string cpfCnpj, CancellationToken ct = default)
    {
        var response = await _httpClient.GetAsync($"customers?cpfCnpj={cpfCnpj}", ct);
        await EnsureSuccessAsync(response, "search customer");

        var result = await response.Content.ReadFromJsonAsync<AsaasListResponse<AsaasCustomer>>(ct);
        return result?.Data?.FirstOrDefault();
    }

    // =========================================================================
    // PAYMENTS
    // =========================================================================

    public async Task<AsaasPayment> CreatePaymentAsync(CreatePaymentRequest request, CancellationToken ct = default)
    {
        _logger.LogInformation("Creating Asaas payment for customer {CustomerId}, type {BillingType}, value {Value}",
            request.Customer, request.BillingType, request.Value);

        var response = await _httpClient.PostAsJsonAsync("payments", request, ct);
        await EnsureSuccessAsync(response, "create payment");

        return await response.Content.ReadFromJsonAsync<AsaasPayment>(ct)
            ?? throw new AsaasException("Failed to deserialize payment response");
    }

    public async Task<AsaasPayment?> GetPaymentAsync(string paymentId, CancellationToken ct = default)
    {
        var response = await _httpClient.GetAsync($"payments/{paymentId}", ct);

        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
            return null;

        await EnsureSuccessAsync(response, "get payment");
        return await response.Content.ReadFromJsonAsync<AsaasPayment>(ct);
    }

    public async Task<AsaasPixQrCode?> GetPixQrCodeAsync(string paymentId, CancellationToken ct = default)
    {
        var response = await _httpClient.GetAsync($"payments/{paymentId}/pixQrCode", ct);

        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
            return null;

        await EnsureSuccessAsync(response, "get pix qrcode");
        return await response.Content.ReadFromJsonAsync<AsaasPixQrCode>(ct);
    }

    // =========================================================================
    // SUBSCRIPTIONS
    // =========================================================================

    public async Task<AsaasSubscription> CreateSubscriptionAsync(CreateSubscriptionRequest request, CancellationToken ct = default)
    {
        _logger.LogInformation("Creating Asaas subscription for customer {CustomerId}", request.Customer);

        var response = await _httpClient.PostAsJsonAsync("subscriptions", request, ct);
        await EnsureSuccessAsync(response, "create subscription");

        return await response.Content.ReadFromJsonAsync<AsaasSubscription>(ct)
            ?? throw new AsaasException("Failed to deserialize subscription response");
    }

    public async Task<AsaasSubscription?> GetSubscriptionAsync(string subscriptionId, CancellationToken ct = default)
    {
        var response = await _httpClient.GetAsync($"subscriptions/{subscriptionId}", ct);

        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
            return null;

        await EnsureSuccessAsync(response, "get subscription");
        return await response.Content.ReadFromJsonAsync<AsaasSubscription>(ct);
    }

    public async Task CancelSubscriptionAsync(string subscriptionId, CancellationToken ct = default)
    {
        _logger.LogInformation("Canceling Asaas subscription {SubscriptionId}", subscriptionId);

        var response = await _httpClient.DeleteAsync($"subscriptions/{subscriptionId}", ct);
        await EnsureSuccessAsync(response, "cancel subscription");
    }

    // =========================================================================
    // HELPERS
    // =========================================================================

    private async Task EnsureSuccessAsync(HttpResponseMessage response, string operation)
    {
        if (!response.IsSuccessStatusCode)
        {
            var error = await response.Content.ReadAsStringAsync();
            _logger.LogError("Asaas API error during {Operation}: {StatusCode} - {Error}",
                operation, response.StatusCode, error);
            throw new AsaasException($"Failed to {operation}: {response.StatusCode} - {error}");
        }
    }
}

// ==============================================================================
// DTOS
// ==============================================================================

public record CreateCustomerRequest
{
    [JsonPropertyName("name")]
    public required string Name { get; init; }

    [JsonPropertyName("cpfCnpj")]
    public required string CpfCnpj { get; init; }

    [JsonPropertyName("email")]
    public string? Email { get; init; }

    [JsonPropertyName("phone")]
    public string? Phone { get; init; }

    [JsonPropertyName("mobilePhone")]
    public string? MobilePhone { get; init; }

    [JsonPropertyName("externalReference")]
    public string? ExternalReference { get; init; }
}

public record CreatePaymentRequest
{
    [JsonPropertyName("customer")]
    public required string Customer { get; init; }

    [JsonPropertyName("billingType")]
    public required string BillingType { get; init; } // BOLETO, PIX, CREDIT_CARD

    [JsonPropertyName("value")]
    public required decimal Value { get; init; }

    [JsonPropertyName("dueDate")]
    public required string DueDate { get; init; } // yyyy-MM-dd

    [JsonPropertyName("description")]
    public string? Description { get; init; }

    [JsonPropertyName("externalReference")]
    public string? ExternalReference { get; init; }
}

public record CreateSubscriptionRequest
{
    [JsonPropertyName("customer")]
    public required string Customer { get; init; }

    [JsonPropertyName("billingType")]
    public required string BillingType { get; init; }

    [JsonPropertyName("value")]
    public required decimal Value { get; init; }

    [JsonPropertyName("nextDueDate")]
    public required string NextDueDate { get; init; }

    [JsonPropertyName("cycle")]
    public required string Cycle { get; init; } // MONTHLY, WEEKLY, BIWEEKLY, YEARLY

    [JsonPropertyName("description")]
    public string? Description { get; init; }

    [JsonPropertyName("externalReference")]
    public string? ExternalReference { get; init; }
}

public record AsaasCustomer
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = string.Empty;

    [JsonPropertyName("name")]
    public string Name { get; init; } = string.Empty;

    [JsonPropertyName("cpfCnpj")]
    public string CpfCnpj { get; init; } = string.Empty;

    [JsonPropertyName("email")]
    public string? Email { get; init; }

    [JsonPropertyName("externalReference")]
    public string? ExternalReference { get; init; }
}

public record AsaasPayment
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = string.Empty;

    [JsonPropertyName("customer")]
    public string Customer { get; init; } = string.Empty;

    [JsonPropertyName("value")]
    public decimal Value { get; init; }

    [JsonPropertyName("status")]
    public string Status { get; init; } = string.Empty;

    [JsonPropertyName("billingType")]
    public string BillingType { get; init; } = string.Empty;

    [JsonPropertyName("dueDate")]
    public string DueDate { get; init; } = string.Empty;

    [JsonPropertyName("invoiceUrl")]
    public string? InvoiceUrl { get; init; }

    [JsonPropertyName("bankSlipUrl")]
    public string? BankSlipUrl { get; init; }

    [JsonPropertyName("externalReference")]
    public string? ExternalReference { get; init; }
}

public record AsaasPixQrCode
{
    [JsonPropertyName("encodedImage")]
    public string? EncodedImage { get; init; }

    [JsonPropertyName("payload")]
    public string? Payload { get; init; }

    [JsonPropertyName("expirationDate")]
    public string? ExpirationDate { get; init; }
}

public record AsaasSubscription
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = string.Empty;

    [JsonPropertyName("customer")]
    public string Customer { get; init; } = string.Empty;

    [JsonPropertyName("value")]
    public decimal Value { get; init; }

    [JsonPropertyName("status")]
    public string Status { get; init; } = string.Empty;

    [JsonPropertyName("cycle")]
    public string Cycle { get; init; } = string.Empty;

    [JsonPropertyName("nextDueDate")]
    public string NextDueDate { get; init; } = string.Empty;

    [JsonPropertyName("externalReference")]
    public string? ExternalReference { get; init; }
}

public record AsaasListResponse<T>
{
    [JsonPropertyName("data")]
    public List<T>? Data { get; init; }

    [JsonPropertyName("totalCount")]
    public int TotalCount { get; init; }
}

// ==============================================================================
// EXCEPTION
// ==============================================================================

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

// ==============================================================================
// DEPENDENCY INJECTION
// ==============================================================================

public static class AsaasServiceExtensions
{
    public static IServiceCollection AddAsaasClient(this IServiceCollection services, IConfiguration configuration)
    {
        services.Configure<AsaasOptions>(configuration.GetSection(AsaasOptions.SectionName));

        services.AddHttpClient<IAsaasClient, AsaasClient>((sp, client) =>
        {
            var options = sp.GetRequiredService<IOptions<AsaasOptions>>().Value;
            client.BaseAddress = new Uri(options.BaseUrl.TrimEnd('/') + "/");
            client.DefaultRequestHeaders.Add("access_token", options.ApiKey);
            client.DefaultRequestHeaders.Add("User-Agent", "MORPH-SPEC/1.0");
        });

        return services;
    }
}
