namespace WalletService.Infrastructure.PaymentGateways;

using System.Globalization;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WalletService.Domain.AggregatesModel.PaymentAggregate;

/// <summary>
/// EN: VN Pay payment gateway implementation (API v2.1.0).
/// VI: Trien khai cong thanh toan VN Pay (API v2.1.0).
/// </summary>
/// <remarks>
/// EN: Supports payment creation, query, refund, and IPN callback validation.
/// Sandbox: https://sandbox.vnpayment.vn/paymentv2/vpcpay.html
/// VI: Ho tro tao thanh toan, truy van, hoan tien, va xac thuc IPN callback.
/// Sandbox: https://sandbox.vnpayment.vn/paymentv2/vpcpay.html
/// </remarks>
public class VnPayGateway : IPaymentGateway
{
    private readonly VnPayOptions _options;
    private readonly HttpClient _httpClient;
    private readonly ILogger<VnPayGateway> _logger;

    /// <summary>
    /// EN: VN Pay API version.
    /// VI: Phien ban API VN Pay.
    /// </summary>
    private const string VnPayVersion = "2.1.0";

    /// <summary>
    /// EN: VN Pay command for payment.
    /// VI: Lenh VN Pay cho thanh toan.
    /// </summary>
    private const string VnPayCommandPay = "pay";

    /// <summary>
    /// EN: VN Pay command for query.
    /// VI: Lenh VN Pay cho truy van.
    /// </summary>
    private const string VnPayCommandQuery = "querydr";

    /// <summary>
    /// EN: VN Pay command for refund.
    /// VI: Lenh VN Pay cho hoan tien.
    /// </summary>
    private const string VnPayCommandRefund = "refund";

    public string GatewayName => "VNPAY";

    public VnPayGateway(
        IOptions<VnPayOptions> options,
        HttpClient httpClient,
        ILogger<VnPayGateway> logger)
    {
        _options = options.Value ?? throw new ArgumentNullException(nameof(options));
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <inheritdoc />
    public Task<PaymentResult> CreatePaymentAsync(PaymentRequest request, CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "EN: Creating VN Pay payment for order {OrderId}, amount {Amount} {Currency} / " +
            "VI: Tao thanh toan VN Pay cho don hang {OrderId}, so tien {Amount} {Currency}",
            request.OrderId, request.Amount, request.Currency);

        var vnpParams = new SortedDictionary<string, string>
        {
            { "vnp_Version", VnPayVersion },
            { "vnp_Command", VnPayCommandPay },
            { "vnp_TmnCode", _options.TmnCode },
            { "vnp_Amount", ((long)(request.Amount * 100)).ToString() },
            { "vnp_CreateDate", DateTime.UtcNow.ToString("yyyyMMddHHmmss") },
            { "vnp_CurrCode", request.Currency.ToUpperInvariant() },
            { "vnp_IpAddr", request.IpAddress },
            { "vnp_Locale", "vn" },
            { "vnp_OrderInfo", request.Description },
            { "vnp_OrderType", "other" },
            { "vnp_ReturnUrl", request.ReturnUrl },
            { "vnp_TxnRef", request.OrderId.ToString("N") },
            { "vnp_ExpireDate", DateTime.UtcNow.AddMinutes(15).ToString("yyyyMMddHHmmss") }
        };

        // EN: Build query string and generate HMAC-SHA512 hash
        // VI: Xay dung query string va tao hash HMAC-SHA512
        var queryString = BuildQueryString(vnpParams);
        var secureHash = ComputeHmacSha512(_options.HashSecret, queryString);

        var paymentUrl = $"{_options.PaymentUrl}?{queryString}&vnp_SecureHash={secureHash}";

        _logger.LogInformation(
            "EN: VN Pay payment URL generated for order {OrderId} / " +
            "VI: Da tao URL thanh toan VN Pay cho don hang {OrderId}",
            request.OrderId);

        return Task.FromResult(new PaymentResult(
            Success: true,
            TransactionId: request.OrderId.ToString("N"),
            PaymentUrl: paymentUrl
        ));
    }

    /// <inheritdoc />
    public async Task<PaymentResult> QueryPaymentAsync(string transactionId, CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "EN: Querying VN Pay transaction {TransactionId} / " +
            "VI: Truy van giao dich VN Pay {TransactionId}",
            transactionId);

        var requestDate = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
        var requestId = Guid.NewGuid().ToString("N");

        var vnpParams = new SortedDictionary<string, string>
        {
            { "vnp_RequestId", requestId },
            { "vnp_Version", VnPayVersion },
            { "vnp_Command", VnPayCommandQuery },
            { "vnp_TmnCode", _options.TmnCode },
            { "vnp_TxnRef", transactionId },
            { "vnp_OrderInfo", $"Query transaction {transactionId}" },
            { "vnp_TransactionDate", requestDate },
            { "vnp_CreateDate", requestDate },
            { "vnp_IpAddr", "127.0.0.1" }
        };

        // EN: Generate hash for query request
        // VI: Tao hash cho yeu cau truy van
        var hashData = $"{requestId}|{VnPayVersion}|{VnPayCommandQuery}|{_options.TmnCode}|{transactionId}|{requestDate}|{requestDate}|127.0.0.1|Query transaction {transactionId}";
        var secureHash = ComputeHmacSha512(_options.HashSecret, hashData);
        vnpParams.Add("vnp_SecureHash", secureHash);

        try
        {
            var response = await _httpClient.PostAsJsonAsync(
                _options.ApiUrl,
                vnpParams,
                cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning(
                    "EN: VN Pay query failed with status {StatusCode} / " +
                    "VI: Truy van VN Pay that bai voi status {StatusCode}",
                    response.StatusCode);

                return new PaymentResult(
                    Success: false,
                    ErrorCode: "QUERY_FAILED",
                    ErrorMessage: $"VN Pay query returned HTTP {(int)response.StatusCode}");
            }

            var content = await response.Content.ReadAsStringAsync(cancellationToken);
            var responseData = JsonSerializer.Deserialize<Dictionary<string, string>>(content);

            var responseCode = responseData?.GetValueOrDefault("vnp_ResponseCode") ?? "99";
            var isSuccess = responseCode == "00";

            return new PaymentResult(
                Success: isSuccess,
                TransactionId: responseData?.GetValueOrDefault("vnp_TransactionNo"),
                ErrorCode: isSuccess ? null : responseCode,
                ErrorMessage: isSuccess ? null : $"VN Pay response code: {responseCode}",
                GatewayResponse: responseData?.ToDictionary(k => k.Key, v => v.Value));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex,
                "EN: Error querying VN Pay transaction {TransactionId} / " +
                "VI: Loi truy van giao dich VN Pay {TransactionId}",
                transactionId);

            return new PaymentResult(
                Success: false,
                ErrorCode: "QUERY_ERROR",
                ErrorMessage: ex.Message);
        }
    }

    /// <inheritdoc />
    public async Task<PaymentResult> RefundAsync(string transactionId, decimal amount, CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "EN: Requesting VN Pay refund for transaction {TransactionId}, amount {Amount} / " +
            "VI: Yeu cau hoan tien VN Pay cho giao dich {TransactionId}, so tien {Amount}",
            transactionId, amount);

        var requestDate = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
        var requestId = Guid.NewGuid().ToString("N");

        var vnpParams = new SortedDictionary<string, string>
        {
            { "vnp_RequestId", requestId },
            { "vnp_Version", VnPayVersion },
            { "vnp_Command", VnPayCommandRefund },
            { "vnp_TmnCode", _options.TmnCode },
            { "vnp_TransactionType", "02" }, // Full refund
            { "vnp_TxnRef", transactionId },
            { "vnp_Amount", ((long)(amount * 100)).ToString() },
            { "vnp_OrderInfo", $"Refund transaction {transactionId}" },
            { "vnp_TransactionDate", requestDate },
            { "vnp_CreateDate", requestDate },
            { "vnp_IpAddr", "127.0.0.1" }
        };

        // EN: Generate hash for refund request
        // VI: Tao hash cho yeu cau hoan tien
        var hashData = $"{requestId}|{VnPayVersion}|{VnPayCommandRefund}|{_options.TmnCode}|02|{transactionId}|{(long)(amount * 100)}|0|{requestDate}|{requestDate}|127.0.0.1|Refund transaction {transactionId}";
        var secureHash = ComputeHmacSha512(_options.HashSecret, hashData);
        vnpParams.Add("vnp_SecureHash", secureHash);

        try
        {
            var response = await _httpClient.PostAsJsonAsync(
                _options.ApiUrl,
                vnpParams,
                cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                return new PaymentResult(
                    Success: false,
                    ErrorCode: "REFUND_FAILED",
                    ErrorMessage: $"VN Pay refund returned HTTP {(int)response.StatusCode}");
            }

            var content = await response.Content.ReadAsStringAsync(cancellationToken);
            var responseData = JsonSerializer.Deserialize<Dictionary<string, string>>(content);

            var responseCode = responseData?.GetValueOrDefault("vnp_ResponseCode") ?? "99";
            var isSuccess = responseCode == "00";

            return new PaymentResult(
                Success: isSuccess,
                TransactionId: responseData?.GetValueOrDefault("vnp_TransactionNo"),
                ErrorCode: isSuccess ? null : responseCode,
                ErrorMessage: isSuccess ? null : $"VN Pay refund response code: {responseCode}",
                GatewayResponse: responseData?.ToDictionary(k => k.Key, v => v.Value));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex,
                "EN: Error requesting VN Pay refund for transaction {TransactionId} / " +
                "VI: Loi yeu cau hoan tien VN Pay cho giao dich {TransactionId}",
                transactionId);

            return new PaymentResult(
                Success: false,
                ErrorCode: "REFUND_ERROR",
                ErrorMessage: ex.Message);
        }
    }

    /// <inheritdoc />
    public bool ValidateCallback(IDictionary<string, string> parameters, string secureHash)
    {
        _logger.LogInformation(
            "EN: Validating VN Pay IPN callback / " +
            "VI: Xac thuc VN Pay IPN callback");

        // EN: Remove the secure hash from parameters before verification
        // VI: Loai bo secure hash khoi cac tham so truoc khi xac thuc
        var vnpParams = new SortedDictionary<string, string>(
            parameters
                .Where(p => p.Key.StartsWith("vnp_") &&
                           p.Key != "vnp_SecureHash" &&
                           p.Key != "vnp_SecureHashType")
                .ToDictionary(p => p.Key, p => p.Value));

        var queryString = BuildQueryString(vnpParams);
        var computedHash = ComputeHmacSha512(_options.HashSecret, queryString);

        var isValid = string.Equals(computedHash, secureHash, StringComparison.OrdinalIgnoreCase);

        if (!isValid)
        {
            _logger.LogWarning(
                "EN: VN Pay IPN callback hash validation failed / " +
                "VI: Xac thuc hash IPN callback VN Pay that bai");
        }

        return isValid;
    }

    #region Private Helpers

    /// <summary>
    /// EN: Build URL-encoded query string from sorted parameters.
    /// VI: Xay dung query string duoc URL-encode tu cac tham so da sap xep.
    /// </summary>
    private static string BuildQueryString(SortedDictionary<string, string> parameters)
    {
        var sb = new StringBuilder();
        foreach (var kvp in parameters)
        {
            if (sb.Length > 0)
                sb.Append('&');
            sb.Append(Uri.EscapeDataString(kvp.Key));
            sb.Append('=');
            sb.Append(Uri.EscapeDataString(kvp.Value));
        }
        return sb.ToString();
    }

    /// <summary>
    /// EN: Compute HMAC-SHA512 hash.
    /// VI: Tinh toan hash HMAC-SHA512.
    /// </summary>
    private static string ComputeHmacSha512(string key, string data)
    {
        var keyBytes = Encoding.UTF8.GetBytes(key);
        var dataBytes = Encoding.UTF8.GetBytes(data);

        using var hmac = new HMACSHA512(keyBytes);
        var hashBytes = hmac.ComputeHash(dataBytes);

        return Convert.ToHexStringLower(hashBytes);
    }

    #endregion
}
