using System.Text;
using System.Text.Json;
using Application.Abstractions.Data;
using Application.Abstractions.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Infrastructure.Payment;

internal sealed class DanalPayService(
    HttpClient httpClient,
    IAppDbContext db,
    ILogger<DanalPayService> logger
) : IDanalPayService
{
    private const string ConfirmUrl = "https://one-api.danalpay.com/payments/confirm";
    private const string CancelUrl = "https://one-api.danalpay.com/payments/cancel";

    public async Task<DanalClientConfig> GetClientConfigAsync(CancellationToken ct)
    {
        var (cpid, clientKey, _) = await GetKeysAsync(ct);
        return new DanalClientConfig(clientKey, cpid);
    }

    public async Task<DanalConfirmResult> ConfirmAsync(string method, string transactionID, string orderID, int amount, CancellationToken ct)
    {
        try
        {
            var (cpid, _, secretKey) = await GetKeysAsync(ct);
            var authHeader = BuildBasicAuth(secretKey);

            var request = new HttpRequestMessage(HttpMethod.Post, ConfirmUrl);
            request.Headers.Add("Authorization", authHeader);

            var body = new
            {
                method,
                transactionId = transactionID,
                orderId = orderID,
                amount = amount.ToString(),
                merchantId = cpid
            };

            request.Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");

            var response = await httpClient.SendAsync(request, ct);
            var json = await response.Content.ReadAsStringAsync(ct);

            logger.LogInformation("[DanalPay] Confirm response: {StatusCode} {Body}", response.StatusCode, json);

            var doc = JsonSerializer.Deserialize<JsonElement>(json);
            var code = doc.TryGetProperty("code", out var codeProp) ? codeProp.GetString() : null;
            var message = doc.TryGetProperty("message", out var msgProp) ? msgProp.GetString() : null;

            string? Get(string key) => doc.TryGetProperty(key, out var p) ? p.GetString() : null;
            int? GetInt(string key) => int.TryParse(Get(key), out var v) ? v : null;
            byte? GetByte(string key) => byte.TryParse(Get(key), out var v) ? v : null;

            return new DanalConfirmResult(
                code == "SUCCESS", code, message,
                Get("transactionId") ?? transactionID,
                Get("orderName"),
                GetInt("totalAmount"),
                GetInt("discountAmount"),
                Get("userName"),
                Get("transDate"), Get("transTime"),
                Get("cardCode"), Get("cardName"), Get("cardNo"),
                GetByte("installmentMonths"), Get("approveNo"),
                Get("approvalDateTime"), Get("authKey"),
                Get("accountNumber"), Get("bankCode"), Get("userId"), Get("userEmail"),
                Get("bankName"), Get("expireDate"), Get("expireTime"),
                Get("virtualAccountNumber"), Get("useCashReceipt")
            );
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[DanalPay] Confirm error for orderID={OrderID}", orderID);
            return new DanalConfirmResult(false, "ERROR", ex.Message, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
        }
    }

    public async Task<DanalCancelResult> CancelAsync(string method, string transactionID, int amount, string cancelType, CancellationToken ct)
    {
        try
        {
            var (cpid, _, secretKey) = await GetKeysAsync(ct);
            var authHeader = BuildBasicAuth(secretKey);

            var request = new HttpRequestMessage(HttpMethod.Post, CancelUrl);
            request.Headers.Add("Authorization", authHeader);

            var body = new
            {
                method,
                transactionId = transactionID,
                amount = amount.ToString(),
                merchantId = cpid,
                cancelType
            };

            request.Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");

            var response = await httpClient.SendAsync(request, ct);
            var json = await response.Content.ReadAsStringAsync(ct);

            logger.LogInformation("[DanalPay] Cancel response: {StatusCode} {Body}", response.StatusCode, json);

            var doc = JsonSerializer.Deserialize<JsonElement>(json);
            var code = doc.TryGetProperty("code", out var codeProp) ? codeProp.GetString() : null;
            var message = doc.TryGetProperty("message", out var msgProp) ? msgProp.GetString() : null;

            string? Get(string key) => doc.TryGetProperty(key, out var p) ? p.GetString() : null;
            int? GetInt(string key) => int.TryParse(Get(key), out var v) ? v : null;

            return new DanalCancelResult(
                code == "SUCCESS", code, message,
                Get("originalTransactionId"),
                GetInt("cancelledAmount"),
                Get("transDate"), Get("transTime"),
                Get("balance"), Get("remainedAmount"),
                Get("approvalDateTime")
            );
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[DanalPay] Cancel error for transactionID={TransactionID}", transactionID);
            return new DanalCancelResult(false, "ERROR", ex.Message, null, null, null, null, null, null, null);
        }
    }

    // ── Private ──────────────────────────────────────────────────────

    private async Task<(string Cpid, string ClientKey, string SecretKey)> GetKeysAsync(CancellationToken ct)
    {
        var config = await db.Config.FirstOrDefaultAsync(ct);
        if (config?.External is null)
        {
            throw new InvalidOperationException("다날 결제 설정이 없습니다. 관리자 페이지에서 설정해주세요.");
        }

        var ext = config.External;
        var isLive = string.Equals(ext.DanalPayMode, "live", StringComparison.OrdinalIgnoreCase);

        var cpid = isLive ? ext.DanalLiveCpid : ext.DanalTestCpid;
        var clientKey = isLive ? ext.DanalLiveClientKeyEnc : ext.DanalTestClientKeyEnc;
        var secretKey = isLive ? ext.DanalLiveSecretKeyEnc : ext.DanalTestSecretKeyEnc;

        if (string.IsNullOrEmpty(cpid) || string.IsNullOrEmpty(clientKey) || string.IsNullOrEmpty(secretKey))
        {
            throw new InvalidOperationException($"다날 {(isLive ? "라이브" : "테스트")} 설정이 불완전합니다.");
        }

        return (cpid, clientKey, secretKey);
    }

    private static string BuildBasicAuth(string secretKey)
    {
        // SecretKey + ":" → Base64
        var raw = $"{secretKey}:";
        var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(raw));
        return $"Basic {base64}";
    }
}