using CMSMicroservice.Application.Common;
using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.Common.Models;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Enums;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.Application.WalletCQ.Commands.VerifyDiscountWalletCharge;

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

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

    public Task<bool> Handle(
        VerifyDiscountWalletChargeCommand request,
        CancellationToken cancellationToken) =>
        _paymentLock.ExecuteAsync(
            PaymentLockScopes.Verify(request.UserId, request.Authority),
            PaymentLockStrategy.WaitForRelease,
            ct => HandleCore(request, ct),
            cancellationToken);

    private async Task<bool> HandleCore(
        VerifyDiscountWalletChargeCommand request,
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Verifying discount wallet charge. UserId: {UserId}, Amount: {Amount}, Authority: {Authority}",
                request.UserId,
                request.Amount,
                request.Authority
            );

            // 1. بررسی کاربر
            var user = await _context.Users
                .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);

            if (user == null)
            {
                _logger.LogWarning("User not found: {UserId}", request.UserId);
                throw new NotFoundException(nameof(User), request.UserId);
            }

            // واکشی PaymentTransaction برای گرفتن مبلغ (تومان) جهت verify
            var paymentTx = await _context.PaymentTransactions
                .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken);
            
            var amountInToman = (decimal)(paymentTx?.Amount ?? 0);

            // 2. Verify با درگاه (مبلغ به تومان — تبدیل به ریال در ZarinPalService)
            var verifyResult = await _paymentGateway.VerifyPaymentAsync(
                request.Authority,
                "OK",  // وقتی این handler فراخوانی میشه یعنی کاربر از درگاه برگشته — Status باید OK باشه
                amountInToman,
                cancellationToken
            );
            if (paymentTx != null)
            {
                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(
                    "Discount wallet charge verification failed for UserId {UserId}: {Message}",
                    request.UserId,
                    verifyResult.Message
                );

                throw new Exception($"تراکنش ناموفق: {verifyResult.Message}");
            }

            // 3. شارژ DiscountBalance
            var wallet = await _context.UserWallets
                .FirstOrDefaultAsync(w => w.UserId == user.Id, cancellationToken);

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

            var oldBalance = wallet.DiscountBalance;
            wallet.DiscountBalance += request.Amount;

            _logger.LogInformation(
                "Charging discount balance for UserId {UserId}: {OldBalance} -> {NewBalance}",
                request.UserId,
                oldBalance,
                wallet.DiscountBalance
            );

            // 4. ثبت Transaction
            var transaction = new Transaction
            {
                Amount = request.Amount,
                Description = $"شارژ کیف پول تخفیفی - کاربر {user.Id}",
                PaymentStatus = PaymentStatus.Success,
                PaymentDate = DateTime.Now,
                RefId = verifyResult.RefId,
                Type = TransactionType.DiscountWalletCharge
            };

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

            // ثبت لاگ تغییرات کیف پول (بعد از ایجاد Transaction برای داشتن TransactionId)
            _context.UserWalletHistories.Add(new Domain.Entities.UserWalletHistory
            {
                WalletId = wallet.Id,
                CurrentBalance = wallet.Balance,
                ChangeValue = 0,
                CurrentNetworkBalance = wallet.NetworkBalance,
                ChangeNerworkValue = 0,
                CurrentDiscountBalance = wallet.DiscountBalance,
                ChangeDiscountValue = request.Amount,
                IsIncrease = true,
                RefrenceId = transaction.Id
            });
            await _context.SaveChangesAsync(cancellationToken);

            // لینک PaymentTransaction به Transaction داخلی
            if (paymentTx != null)
            {
                paymentTx.TransactionId = transaction.Id;
                await _context.SaveChangesAsync(cancellationToken);
            }

            _logger.LogInformation(
                "Discount wallet charged successfully. UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}",
                user.Id,
                transaction.Id,
                verifyResult.RefId
            );

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