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;
using ValidationException = FluentValidation.ValidationException;

namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyPackagePurchase;

public class VerifyPackagePurchaseCommandHandler 
    : IRequestHandler<VerifyPackagePurchaseCommand, bool>
{
    private readonly IApplicationDbContext _context;
    private readonly IPaymentGatewayService _paymentGateway;
    private readonly ILogger<VerifyPackagePurchaseCommandHandler> _logger;

    public VerifyPackagePurchaseCommandHandler(
        IApplicationDbContext context,
        IPaymentGatewayService paymentGateway,
        ILogger<VerifyPackagePurchaseCommandHandler> logger)
    {
        _context = context;
        _paymentGateway = paymentGateway;
        _logger = logger;
    }

    public async Task<bool> Handle(
        VerifyPackagePurchaseCommand request, 
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Verifying package purchase. OrderId: {OrderId}, Authority: {Authority}",
                request.OrderId,
                request.Authority
            );

            // 1. پیدا کردن سفارش
            var order = await _context.UserOrders
                .Include(o => o.Package)
                .Include(o => o.User)
                .FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken);

            if (order == null)
            {
                _logger.LogWarning("Order not found: {OrderId}", request.OrderId);
                throw new NotFoundException(nameof(UserOrder), request.OrderId);
            }

            // 2. بررسی اینکه سفارش قبلاً پرداخت نشده باشد
            if (order.PaymentStatus == PaymentStatus.Success)
            {
                _logger.LogWarning("Order {OrderId} is already paid", request.OrderId);
                return true;
            }

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

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

            if (!verifyResult.IsSuccess)
            {
                _logger.LogWarning(
                    "Payment verification failed for OrderId {OrderId}: {Message}",
                    request.OrderId,
                    verifyResult.Message
                );

                order.PaymentStatus = PaymentStatus.Reject;
                await _context.SaveChangesAsync(cancellationToken);

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

            // 4. شارژ کیف پول کاربر
            var wallet = await _context.UserWallets
                .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);

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

            // شارژ Balance (موجودی عادی)
            var oldBalance = wallet.Balance;
            wallet.Balance += order.Amount;

            _logger.LogInformation(
                "Charging Balance for UserId {UserId}: {OldBalance} -> {NewBalance}",
                order.UserId,
                oldBalance,
                wallet.Balance
            );

            // شارژ DiscountBalance (موجودی تخفیف) — ضریب تخفیف از پکیج
            var oldDiscountBalance = wallet.DiscountBalance;
            if (order.Package == null)
                throw new NotFoundException("پکیج مرتبط با سفارش یافت نشد");
            var discountMultiplier = order.Package.DiscountMultiplier;
            var discountAmount = (long)(order.Amount * discountMultiplier);
            wallet.DiscountBalance += discountAmount;

            _logger.LogInformation(
                "Charging DiscountBalance for UserId {UserId}: {OldBalance} -> {NewBalance}",
                order.UserId,
                oldDiscountBalance,
                wallet.DiscountBalance
            );

            // 5. ثبت Transaction
            var transaction = new Transaction
            {
                Amount = order.Amount,
                Description = $"خرید پکیج از درگاه - سفارش #{order.Id}",
                PaymentStatus = PaymentStatus.Success,
                PaymentDate = DateTime.Now,
                RefId = verifyResult.RefId,
                Type = TransactionType.DepositIpg
            };

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

            // 6. ثبت لاگ تغییر Balance
            var balanceLog = new UserWalletHistory
            {
                WalletId = wallet.Id,
                CurrentBalance = wallet.Balance,
                ChangeValue = order.Amount,
                CurrentNetworkBalance = wallet.NetworkBalance,
                ChangeNerworkValue = 0,
                CurrentDiscountBalance = wallet.DiscountBalance - discountAmount, // قبل از شارژ DiscountBalance
                ChangeDiscountValue = 0,
                IsIncrease = true,
                RefrenceId = transaction.Id,
                PackageId = order.PackageId
            };
            await _context.UserWalletHistories.AddAsync(balanceLog, cancellationToken);

            // 7. ثبت لاگ تغییر DiscountBalance
            var discountLog = new UserWalletHistory
            {
                WalletId = wallet.Id,
                CurrentBalance = wallet.Balance,
                ChangeValue = 0,
                CurrentNetworkBalance = wallet.NetworkBalance,
                ChangeNerworkValue = 0,
                CurrentDiscountBalance = wallet.DiscountBalance,
                ChangeDiscountValue = discountAmount,
                IsIncrease = true,
                RefrenceId = transaction.Id,
                PackageId = order.PackageId
            };
            await _context.UserWalletHistories.AddAsync(discountLog, cancellationToken);

            // 8. ثبت UserPackagePurchase
            if (order.PackageId.HasValue)
            {
                var packagePurchase = new UserPackagePurchase
                {
                    UserId = order.UserId,
                    PackageId = order.PackageId.Value,
                    PurchaseMethod = PackagePurchaseMethod.DirectPurchase,
                    PurchasedAt = DateTime.Now,
                    Amount = order.Amount,
                    OrderId = order.Id,
                    TransactionId = transaction.Id
                };
                await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken);

                _logger.LogInformation(
                    "Created UserPackagePurchase for UserId {UserId}, PackageId {PackageId}",
                    order.UserId, order.PackageId.Value);
            }

            // 9. به‌روزرسانی Order
            order.TransactionId = transaction.Id;
            order.PaymentStatus = PaymentStatus.Success;
            order.PaymentDate = DateTime.Now;
            order.PaymentMethod = PaymentMethod.IPG;

            // 10. تغییر User.PackagePurchaseMethod
            order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;

            await _context.SaveChangesAsync(cancellationToken);

            _logger.LogInformation(
                "Package purchase verified successfully. " +
                "OrderId: {OrderId}, UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}",
                order.Id,
                order.UserId,
                transaction.Id,
                verifyResult.RefId
            );

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