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.ManualPaymentCQ.Commands.ApproveManualPayment;

public class ApproveManualPaymentCommandHandler : IRequestHandler<ApproveManualPaymentCommand, bool>
{
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUser;
    private readonly ILogger<ApproveManualPaymentCommandHandler> _logger;

    public ApproveManualPaymentCommandHandler(
        IApplicationDbContext context,
        ICurrentUserService currentUser,
        ILogger<ApproveManualPaymentCommandHandler> logger)
    {
        _context = context;
        _currentUser = currentUser;
        _logger = logger;
    }

    public async Task<bool> Handle(
        ApproveManualPaymentCommand request,
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Approving manual payment: {ManualPaymentId}",
                request.ManualPaymentId
            );

            // 1. پیدا کردن ManualPayment
            var manualPayment = await _context.ManualPayments
                .Include(m => m.User)
                .FirstOrDefaultAsync(m => m.Id == request.ManualPaymentId, cancellationToken);

            if (manualPayment == null)
            {
                _logger.LogWarning("ManualPayment not found: {Id}", request.ManualPaymentId);
                throw new NotFoundException(nameof(ManualPayment), request.ManualPaymentId);
            }

            // 2. بررسی وضعیت
            if (manualPayment.Status != ManualPaymentStatus.Pending)
            {
                _logger.LogWarning(
                    "ManualPayment {Id} is not in Pending status: {Status}",
                    request.ManualPaymentId,
                    manualPayment.Status
                );
                throw new BadRequestException($"فقط درخواست‌های در وضعیت Pending قابل تایید هستند. وضعیت فعلی: {manualPayment.Status}");
            }

            // 3. بررسی SuperAdmin
            var currentUserId = _currentUser.UserId;
            if (string.IsNullOrEmpty(currentUserId))
            {
                throw new UnauthorizedAccessException("کاربر احراز هویت نشده است");
            }

            if (!long.TryParse(currentUserId, out var approvedById))
            {
                throw new UnauthorizedAccessException("شناسه کاربر نامعتبر است");
            }

            // 4. پیدا کردن Wallet کاربر
            var wallet = await _context.UserWallets
                .FirstOrDefaultAsync(w => w.UserId == manualPayment.UserId, cancellationToken);

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

            // 5. ایجاد Transaction
            var transaction = new Transaction
            {
                Amount = manualPayment.Amount,
                Description = $"پرداخت دستی - {manualPayment.Type} - {manualPayment.Description}",
                PaymentStatus = PaymentStatus.Success,
                PaymentDate = DateTime.Now,
                RefId = manualPayment.ReferenceNumber,
                Type = MapToTransactionType(manualPayment.Type)
            };

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

            // 6. اعمال تغییرات بر کیف پول
            var oldBalance = wallet.Balance;
            var oldDiscountBalance = wallet.DiscountBalance;
            var oldNetworkBalance = wallet.NetworkBalance;

            switch (manualPayment.Type)
            {
                case ManualPaymentType.CashDeposit:
                case ManualPaymentType.Settlement:
                case ManualPaymentType.ErrorCorrection:
                    wallet.Balance += manualPayment.Amount;
                    wallet.DiscountBalance += manualPayment.Amount;
                    
                    // لاگ Balance
                    await _context.UserWalletHistories.AddAsync(new UserWalletHistory
                    {
                        WalletId = wallet.Id,
                        CurrentBalance = wallet.Balance,
                        ChangeValue = manualPayment.Amount,
                        CurrentNetworkBalance = wallet.NetworkBalance,
                        ChangeNerworkValue = 0,
                        CurrentDiscountBalance = oldDiscountBalance,
                        ChangeDiscountValue = 0,
                        IsIncrease = true,
                        RefrenceId = transaction.Id
                    }, cancellationToken);
                    
                    // لاگ DiscountBalance
                    await _context.UserWalletHistories.AddAsync(new UserWalletHistory
                    {
                        WalletId = wallet.Id,
                        CurrentBalance = wallet.Balance,
                        ChangeValue = 0,
                        CurrentNetworkBalance = wallet.NetworkBalance,
                        ChangeNerworkValue = 0,
                        CurrentDiscountBalance = wallet.DiscountBalance,
                        ChangeDiscountValue = manualPayment.Amount,
                        IsIncrease = true,
                        RefrenceId = transaction.Id
                    }, cancellationToken);
                    break;

                case ManualPaymentType.DiscountWalletCharge:
                    wallet.DiscountBalance += manualPayment.Amount;
                    
                    await _context.UserWalletHistories.AddAsync(new UserWalletHistory
                    {
                        WalletId = wallet.Id,
                        CurrentBalance = wallet.Balance,
                        ChangeValue = 0,
                        CurrentNetworkBalance = wallet.NetworkBalance,
                        ChangeNerworkValue = 0,
                        CurrentDiscountBalance = wallet.DiscountBalance,
                        ChangeDiscountValue = manualPayment.Amount,
                        IsIncrease = true,
                        RefrenceId = transaction.Id
                    }, cancellationToken);
                    break;

                case ManualPaymentType.NetworkWalletCharge:
                    wallet.NetworkBalance += manualPayment.Amount;
                    
                    await _context.UserWalletHistories.AddAsync(new UserWalletHistory
                    {
                        WalletId = wallet.Id,
                        CurrentBalance = wallet.Balance,
                        ChangeValue = 0,
                        CurrentNetworkBalance = wallet.NetworkBalance,
                        ChangeNerworkValue = manualPayment.Amount,
                        CurrentDiscountBalance = wallet.DiscountBalance,
                        ChangeDiscountValue = 0,
                        IsIncrease = true,
                        RefrenceId = transaction.Id
                    }, cancellationToken);
                    break;

                case ManualPaymentType.Refund:
                    // بازگشت وجه - کم کردن از Balance و DiscountBalance
                    if (wallet.Balance < manualPayment.Amount)
                    {
                        throw new BadRequestException("موجودی کیف پول برای بازگشت وجه کافی نیست");
                    }
                    
                    wallet.Balance -= manualPayment.Amount;
                    if (wallet.DiscountBalance >= manualPayment.Amount)
                    {
                        wallet.DiscountBalance -= manualPayment.Amount;
                    }
                    
                    await _context.UserWalletHistories.AddAsync(new UserWalletHistory
                    {
                        WalletId = wallet.Id,
                        CurrentBalance = wallet.Balance,
                        ChangeValue = manualPayment.Amount,
                        CurrentNetworkBalance = wallet.NetworkBalance,
                        ChangeNerworkValue = 0,
                        CurrentDiscountBalance = wallet.DiscountBalance,
                        ChangeDiscountValue = wallet.DiscountBalance < oldDiscountBalance ? manualPayment.Amount : 0,
                        IsIncrease = false,
                        RefrenceId = transaction.Id
                    }, cancellationToken);
                    break;

                default:
                    // Other یا سایر موارد - فقط Balance
                    wallet.Balance += manualPayment.Amount;
                    
                    await _context.UserWalletHistories.AddAsync(new UserWalletHistory
                    {
                        WalletId = wallet.Id,
                        CurrentBalance = wallet.Balance,
                        ChangeValue = manualPayment.Amount,
                        CurrentNetworkBalance = wallet.NetworkBalance,
                        ChangeNerworkValue = 0,
                        CurrentDiscountBalance = wallet.DiscountBalance,
                        ChangeDiscountValue = 0,
                        IsIncrease = true,
                        RefrenceId = transaction.Id
                    }, cancellationToken);
                    break;
            }

            // 7. به‌روزرسانی ManualPayment
            manualPayment.Status = ManualPaymentStatus.Approved;
            manualPayment.ApprovedBy = approvedById;
            manualPayment.ApprovedAt = DateTime.Now;
            manualPayment.TransactionId = transaction.Id;

            await _context.SaveChangesAsync(cancellationToken);

            _logger.LogInformation(
                "Manual payment approved successfully. Id: {Id}, UserId: {UserId}, Amount: {Amount}, ApprovedBy: {ApprovedBy}",
                manualPayment.Id,
                manualPayment.UserId,
                manualPayment.Amount,
                approvedById
            );

            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(
                ex,
                "Error approving manual payment: {ManualPaymentId}",
                request.ManualPaymentId
            );
            throw;
        }
    }

    private TransactionType MapToTransactionType(ManualPaymentType type)
    {
        return type switch
        {
            ManualPaymentType.CashDeposit => TransactionType.DepositExternal1,
            ManualPaymentType.DiscountWalletCharge => TransactionType.DiscountWalletCharge,
            ManualPaymentType.NetworkWalletCharge => TransactionType.NetworkCommission,
            ManualPaymentType.Settlement => TransactionType.DepositExternal1,
            ManualPaymentType.ErrorCorrection => TransactionType.DepositExternal1,
            ManualPaymentType.Refund => TransactionType.Withdraw,
            _ => TransactionType.DepositExternal1
        };
    }
}
