using CMSMicroservice.Application.Common;
using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Common;
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.WalletCQ.Commands.VerifyMagicWalletCharge;

public class VerifyMagicWalletChargeCommandHandler
    : IRequestHandler<VerifyMagicWalletChargeCommand, bool>
{
    private readonly IApplicationDbContext _context;
    private readonly IPaymentGatewayService _paymentGateway;
    private readonly ILogger<VerifyMagicWalletChargeCommandHandler> _logger;
    private readonly IUserPaymentLock _paymentLock;

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

    public async Task<bool> Handle(
        VerifyMagicWalletChargeCommand request,
        CancellationToken cancellationToken)
    {
        var paymentTx = await _context.PaymentTransactions
            .AsNoTracking()
            .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken);

        if (paymentTx == null)
            throw new NotFoundException("تراکنش پرداخت یافت نشد");

        if (!paymentTx.UserId.HasValue)
            throw new BadRequestException("شناسه کاربر در تراکنش پرداخت یافت نشد");

        return await _paymentLock.ExecuteAsync(
            PaymentLockScopes.Verify(paymentTx.UserId.Value, request.Authority),
            PaymentLockStrategy.WaitForRelease,
            ct => HandleCore(request, ct),
            cancellationToken);
    }

    private async Task<bool> HandleCore(
        VerifyMagicWalletChargeCommand request,
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Verifying magic wallet charge. Authority: {Authority}, Status: {Status}",
                request.Authority,
                request.Status
            );

            // 1. پیدا کردن PaymentTransaction از Authority
            var paymentTx = await _context.PaymentTransactions
                .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken);

            if (paymentTx == null)
            {
                _logger.LogError("PaymentTransaction not found for Authority: {Authority}", request.Authority);
                throw new NotFoundException("تراکنش پرداخت یافت نشد");
            }

            if (paymentTx.PaymentStatus)
            {
                _logger.LogWarning("PaymentTransaction already verified: {Authority}", request.Authority);
                return true; // قبلاً تأیید شده
            }

            var userId = paymentTx.UserId
                ?? throw new BadRequestException("شناسه کاربر در تراکنش پرداخت یافت نشد");
            var depositAmount = paymentTx.Amount; // مبلغ واقعی واریزی (تومان)

            // 2. بررسی وضعیت برگشتی از درگاه
            if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase))
            {
                _logger.LogWarning(
                    "Magic charge cancelled by user. UserId: {UserId}, Authority: {Authority}",
                    userId, request.Authority
                );

                paymentTx.PaymentStatus = false;
                paymentTx.VerificationStatusMessage = "پرداخت توسط کاربر لغو شد";
                await _context.SaveChangesAsync(cancellationToken);

                throw new BadRequestException("پرداخت توسط کاربر لغو شد");
            }

            // 3. Verify با درگاه (مبلغ به تومان — تبدیل به ریال در ZarinPalService)
            var verifyResult = await _paymentGateway.VerifyPaymentAsync(
                request.Authority,
                request.Status,
                depositAmount,
                cancellationToken
            );

            // آپدیت PaymentTransaction
            paymentTx.PaymentStatus = verifyResult.IsSuccess;
            paymentTx.VerificationStatusCode = verifyResult.VerificationCode;
            paymentTx.VerificationStatusMessage = verifyResult.Message;
            paymentTx.CardPan = verifyResult.CardPan;
            paymentTx.CardHash = verifyResult.CardHash;
            paymentTx.RefId = verifyResult.TrackingCode;

            if (!verifyResult.IsSuccess)
            {
                _logger.LogWarning(
                    "Magic wallet charge verification failed for UserId {UserId}: {Message}",
                    userId,
                    verifyResult.Message
                );

                await _context.SaveChangesAsync(cancellationToken);
                throw new BadRequestException($"تراکنش ناموفق: {verifyResult.Message}");
            }

            // 4. بررسی کیف‌پول و حالت جادویی
            var wallet = await _context.UserWallets
                .FirstOrDefaultAsync(w => w.UserId == userId, cancellationToken);

            if (wallet == null)
            {
                _logger.LogError("Wallet not found for UserId: {UserId}", userId);
                throw new NotFoundException("کیف پول کاربر یافت نشد");
            }

            if (wallet.WalletMode != WalletMode.Magic)
            {
                _logger.LogError(
                    "Wallet is not in Magic mode during verify. UserId: {UserId}, Mode: {Mode}",
                    userId, wallet.WalletMode
                );
                throw new BadRequestException("کیف‌پول در حالت جادویی نیست");
            }

            // 4.5. بارگذاری پکیج کاربر برای ضریب جادویی
            var currentCycle = await _context.ClubMembershipCycles
                .FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, cancellationToken);
            var package = currentCycle != null
                ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, cancellationToken)
                : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken);
            if (package == null)
                throw new NotFoundException("پکیج یافت نشد");

            // 5. محاسبه اعتبار
            var creditAmount = (long)(depositAmount * package.MagicWalletMultiplier); // مبلغ × 2.5
            var bonusAmount = creditAmount - depositAmount; // بونوس = مبلغ × 1.5

            // 6. ثبت تراکنش واریز واقعی (MagicWalletDeposit)
            var depositTransaction = new Transaction
            {
                Amount = depositAmount,
                Description = $"شارژ کیف‌پول جادویی - واریز واقعی - کاربر {userId}",
                PaymentStatus = PaymentStatus.Success,
                PaymentDate = DateTime.UtcNow,
                RefId = verifyResult.RefId,
                Type = TransactionType.MagicWalletDeposit
            };

            _context.Transactions.Add(depositTransaction);
            await _context.SaveChangesAsync(cancellationToken);

            // 7. ثبت تراکنش بونوس (MagicWalletBonus)
            var bonusTransaction = new Transaction
            {
                Amount = bonusAmount,
                Description = $"شارژ کیف‌پول جادویی - بونوس ×{package.MagicWalletMultiplier - 1} - کاربر {userId}",
                PaymentStatus = PaymentStatus.Success,
                PaymentDate = DateTime.UtcNow,
                RefId = $"MAGIC_BONUS_{depositTransaction.Id}",
                Type = TransactionType.MagicWalletBonus
            };

            _context.Transactions.Add(bonusTransaction);
            await _context.SaveChangesAsync(cancellationToken);

            // 8. شارژ Balance و آپدیت شمارنده‌ها
            wallet.Balance += creditAmount;
            wallet.MagicTotalDeposited += depositAmount;
            wallet.MagicTotalCredited += creditAmount;

            // 9. ثبت WalletHistory (اجباری)
            var walletLog = new UserWalletHistory
            {
                WalletId = wallet.Id,
                CurrentBalance = wallet.Balance,
                ChangeValue = creditAmount,
                CurrentNetworkBalance = wallet.NetworkBalance,
                ChangeNerworkValue = 0,
                CurrentDiscountBalance = wallet.DiscountBalance,
                ChangeDiscountValue = 0,
                IsIncrease = true,
                RefrenceId = depositTransaction.Id
            };

            _context.UserWalletHistories.Add(walletLog);

            // 10. لینک PaymentTransaction به Transaction داخلی
            paymentTx.TransactionId = depositTransaction.Id;

            await _context.SaveChangesAsync(cancellationToken);

            _logger.LogInformation(
                "Magic wallet charged successfully. UserId: {UserId}, " +
                "Deposit: {Deposit}, Credit: {Credit} (×{Multiplier}), Bonus: {Bonus}, " +
                "TotalDeposited: {TotalDeposited}/{MaxDeposit}, NewBalance: {NewBalance}",
                userId,
                depositAmount,
                creditAmount,
                package.MagicWalletMultiplier,
                bonusAmount,
                wallet.MagicTotalDeposited,
                package.MagicWalletMaxDeposit,
                wallet.Balance
            );

            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(
                ex,
                "Error in VerifyMagicWalletChargeCommand. Authority: {Authority}",
                request.Authority
            );
            throw;
        }
    }
}
