using System.Data;
using CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership;
using CMSMicroservice.Application.Common;
using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Entities.Payment;
using CMSMicroservice.Domain.Enums;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyUserPackagePurchasePayment;

public class VerifyUserPackagePurchasePaymentCommandHandler
    : IRequestHandler<VerifyUserPackagePurchasePaymentCommand, VerifyUserPackagePurchasePaymentResult>
{
    private readonly IApplicationDbContext _context;
    private readonly IPaymentGatewayService _paymentGateway;
    private readonly ISender _sender;
    private readonly IUserPaymentLock _paymentLock;
    private readonly ILogger<VerifyUserPackagePurchasePaymentCommandHandler> _logger;

    public VerifyUserPackagePurchasePaymentCommandHandler(
        IApplicationDbContext context,
        IPaymentGatewayService paymentGateway,
        ISender sender,
        IUserPaymentLock paymentLock,
        ILogger<VerifyUserPackagePurchasePaymentCommandHandler> logger)
    {
        _context = context;
        _paymentGateway = paymentGateway;
        _sender = sender;
        _paymentLock = paymentLock;
        _logger = logger;
    }

    public async Task<VerifyUserPackagePurchasePaymentResult> Handle(
        VerifyUserPackagePurchasePaymentCommand request,
        CancellationToken cancellationToken)
    {
        var purchaseUserId = await _context.UserPackagePurchases
            .Where(p => p.Id == request.PurchaseId && !p.IsDeleted)
            .Select(p => (long?)p.UserId)
            .FirstOrDefaultAsync(cancellationToken);

        if (purchaseUserId is null or 0)
            throw new NotFoundException("سفارش یافت نشد");

        var verifyKey = !string.IsNullOrEmpty(request.Authority)
            ? request.Authority
            : request.PurchaseId.ToString();

        return await _paymentLock.ExecuteAsync(
            PaymentLockScopes.Verify(purchaseUserId.Value, verifyKey),
            PaymentLockStrategy.WaitForRelease,
            ct => HandleCoreAsync(request, ct),
            cancellationToken);
    }

    private async Task<VerifyUserPackagePurchasePaymentResult> HandleCoreAsync(
        VerifyUserPackagePurchasePaymentCommand request,
        CancellationToken ct)
    {
        var purchase = await _context.UserPackagePurchases
            .Include(p => p.Package)
            .Where(p => p.Id == request.PurchaseId && !p.IsDeleted)
            .FirstOrDefaultAsync(ct);

        if (purchase == null)
            throw new NotFoundException("سفارش یافت نشد");

        var transaction = purchase.TransactionId.HasValue
            ? await _context.Transactions
                .FirstOrDefaultAsync(t => t.Id == purchase.TransactionId.Value, ct)
            : null;

        var paymentTx = !string.IsNullOrEmpty(request.Authority)
            ? await _context.PaymentTransactions
                .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, ct)
            : null;

        if (IsPaymentAlreadyCompleted(transaction, paymentTx))
        {
            return BuildResult(purchase, transaction, paymentTx, alreadyPaid: true);
        }

        if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase))
        {
            if (transaction != null && transaction.PaymentStatus != PaymentStatus.Success)
            {
                transaction.PaymentStatus = PaymentStatus.Reject;
                await _context.SaveChangesAsync(ct);
            }

            return new VerifyUserPackagePurchasePaymentResult
            {
                Success = false,
                Message = "پرداخت توسط کاربر لغو شد",
                PackageId = purchase.PackageId,
                PackageName = purchase.Package?.Title ?? string.Empty,
                AmountPaid = purchase.Amount,
                PurchasedAt = purchase.PurchasedAt
            };
        }

        var amountInToman = paymentTx?.Amount ?? purchase.Amount;
        var verifyResult = await _paymentGateway.VerifyPaymentAsync(
            request.Authority, request.Status, (decimal)amountInToman, ct);

        if (!verifyResult.IsSuccess)
        {
            if (paymentTx != null)
            {
                paymentTx.PaymentStatus = false;
                paymentTx.VerificationStatusCode = verifyResult.VerificationCode;
                paymentTx.VerificationStatusMessage = verifyResult.Message;
            }

            if (transaction != null)
                transaction.PaymentStatus = PaymentStatus.Reject;

            await _context.SaveChangesAsync(ct);

            return new VerifyUserPackagePurchasePaymentResult
            {
                Success = false,
                Message = verifyResult.Message ?? "خرید پکیج ناموفق بود",
                TransactionId = transaction?.Id ?? 0,
                PackageId = purchase.PackageId,
                PackageName = purchase.Package?.Title ?? string.Empty,
                AmountPaid = purchase.Amount,
                PurchasedAt = purchase.PurchasedAt
            };
        }

        await using var dbTx = await _context.Database.BeginTransactionAsync(IsolationLevel.Serializable, ct);
        try
        {
            if (purchase.TransactionId.HasValue)
            {
                transaction = await _context.Transactions
                    .FirstOrDefaultAsync(t => t.Id == purchase.TransactionId.Value, ct);
            }

            if (!string.IsNullOrEmpty(request.Authority))
            {
                paymentTx = await _context.PaymentTransactions
                    .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, ct);
            }

            if (IsPaymentAlreadyCompleted(transaction, paymentTx))
            {
                await dbTx.CommitAsync(ct);
                return BuildResult(purchase, transaction, paymentTx, alreadyPaid: true);
            }

            if (transaction == null)
                throw new InvalidOperationException("تراکنش سفارش یافت نشد");

            var alreadyCredited = await _context.UserWalletHistories
                .AnyAsync(h => h.RefrenceId == transaction.Id && !h.IsDeleted, ct);

            if (alreadyCredited)
            {
                transaction.PaymentStatus = PaymentStatus.Success;
                transaction.PaymentDate ??= DateTime.UtcNow;
                UpdatePaymentTransaction(paymentTx, verifyResult, markSuccess: true);
                await _context.SaveChangesAsync(ct);
                await dbTx.CommitAsync(ct);
                return BuildResult(purchase, transaction, paymentTx, alreadyPaid: true);
            }

            UpdatePaymentTransaction(paymentTx, verifyResult, markSuccess: true);

            transaction.PaymentStatus = PaymentStatus.Success;
            transaction.PaymentDate = DateTime.UtcNow;
            transaction.RefId = verifyResult.RefId;

            var wallet = await _context.UserWallets
                .FirstOrDefaultAsync(w => w.UserId == purchase.UserId, ct);

            if (wallet == null)
            {
                wallet = new UserWallet
                {
                    UserId = purchase.UserId,
                    Balance = 0,
                    DiscountBalance = 0,
                    NetworkBalance = 0
                };
                _context.UserWallets.Add(wallet);
                await _context.SaveChangesAsync(ct);
            }

            var discountMultiplier = purchase.Package?.DiscountMultiplier
                ?? throw new InvalidOperationException("پکیج سفارش یافت نشد");
            var discountAmount = (long)(purchase.Amount * (double)discountMultiplier);
            wallet.Balance += purchase.Amount;
            wallet.DiscountBalance += discountAmount;

            _context.UserWalletHistories.Add(new UserWalletHistory
            {
                WalletId = wallet.Id,
                CurrentBalance = wallet.Balance,
                ChangeValue = purchase.Amount,
                CurrentNetworkBalance = wallet.NetworkBalance,
                ChangeNerworkValue = 0,
                CurrentDiscountBalance = wallet.DiscountBalance,
                ChangeDiscountValue = discountAmount,
                IsIncrease = true,
                RefrenceId = transaction.Id,
                PackageId = purchase.PackageId
            });

            var user = await _context.Users
                .FirstOrDefaultAsync(u => u.Id == purchase.UserId, ct);
            if (user != null)
                user.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;

            await _context.SaveChangesAsync(ct);
            await dbTx.CommitAsync(ct);
        }
        catch
        {
            await dbTx.RollbackAsync(ct);
            throw;
        }

        try
        {
            await _sender.Send(new ActivateClubMembershipCommand
            {
                UserId = purchase.UserId,
                ForceActivation = false
            }, ct);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(
                ex,
                "Auto club activation failed after package verify. UserId={UserId}, PurchaseId={PurchaseId}",
                purchase.UserId,
                purchase.Id);
        }

        _logger.LogInformation(
            "Package purchase verified. PurchaseId={PurchaseId}, UserId={UserId}, Authority={Authority}",
            purchase.Id,
            purchase.UserId,
            request.Authority);

        return BuildResult(
            purchase, transaction, paymentTx, alreadyPaid: false, verifyResult.RefId);
    }

    private static bool IsPaymentAlreadyCompleted(Transaction? transaction, PaymentTransaction? paymentTx)
    {
        return transaction?.PaymentStatus == PaymentStatus.Success
            || paymentTx?.PaymentStatus == true;
    }

    private static void UpdatePaymentTransaction(
        PaymentTransaction? paymentTx,
        PaymentVerificationResult verifyResult,
        bool markSuccess)
    {
        if (paymentTx == null) return;

        paymentTx.PaymentStatus = markSuccess && verifyResult.IsSuccess;
        paymentTx.VerificationStatusCode = verifyResult.VerificationCode;
        paymentTx.VerificationStatusMessage = verifyResult.Message;
        paymentTx.CardPan = verifyResult.CardPan;
        paymentTx.CardHash = verifyResult.CardHash;
        paymentTx.RefId = verifyResult.TrackingCode;
    }

    private static VerifyUserPackagePurchasePaymentResult BuildResult(
        UserPackagePurchase purchase,
        Transaction? transaction,
        PaymentTransaction? paymentTx,
        bool alreadyPaid,
        string? referenceCode = null)
    {
        var refCode = referenceCode
            ?? paymentTx?.RefId
            ?? transaction?.RefId
            ?? string.Empty;

        return new VerifyUserPackagePurchasePaymentResult
        {
            Success = true,
            AlreadyPaid = alreadyPaid,
            Message = alreadyPaid ? "پرداخت قبلاً تأیید شده بود" : "خرید پکیج با موفقیت تایید شد",
            TransactionId = transaction?.Id ?? 0,
            ReferenceCode = refCode,
            PackageId = purchase.PackageId,
            PackageName = purchase.Package?.Title ?? string.Empty,
            AmountPaid = purchase.Amount,
            PurchasedAt = purchase.PurchasedAt
        };
    }
}
