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.Configuration;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeMagicWallet;

public class ChargeMagicWalletCommandHandler
    : IRequestHandler<ChargeMagicWalletCommand, PaymentInitiateResult>
{
    private readonly IApplicationDbContext _context;
    private readonly IPaymentGatewayService _paymentGateway;
    private readonly IConfiguration _configuration;
    private readonly ILogger<ChargeMagicWalletCommandHandler> _logger;
    private readonly IUserPaymentLock _paymentLock;

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

    public Task<PaymentInitiateResult> Handle(
        ChargeMagicWalletCommand request,
        CancellationToken cancellationToken) =>
        _paymentLock.ExecuteAsync(
            PaymentLockScopes.Initiate(request.UserId),
            PaymentLockStrategy.FailFast,
            ct => HandleCore(request, ct),
            cancellationToken);

    private async Task<PaymentInitiateResult> HandleCore(
        ChargeMagicWalletCommand request,
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Initiating magic wallet charge for UserId: {UserId}, Amount: {Amount}",
                request.UserId,
                request.Amount
            );

            // 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);
            }

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

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

            // 3. بررسی حالت جادویی
            if (wallet.WalletMode != WalletMode.Magic)
            {
                _logger.LogWarning(
                    "User {UserId} wallet is not in Magic mode (current: {Mode})",
                    request.UserId,
                    wallet.WalletMode
                );
                throw new BadRequestException(
                    "شارژ جادویی فقط در حالت کیف‌پول جادویی امکان‌پذیر است"
                );
            }

            // 3.5. بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی
            var currentCycle = await _context.ClubMembershipCycles
                .FirstOrDefaultAsync(c => c.UserId == request.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("پکیج یافت نشد");

            // 4. بررسی سقف واریزی (per-cycle)
            var remainingDeposit = package.MagicWalletMaxDeposit - wallet.MagicTotalDeposited;

            if (remainingDeposit <= 0)
            {
                _logger.LogWarning(
                    "User {UserId} has reached magic deposit cap. TotalDeposited: {TotalDeposited}",
                    request.UserId,
                    wallet.MagicTotalDeposited
                );
                throw new BadRequestException(
                    "سقف شارژ جادویی در این دور پر شده است"
                );
            }

            if (request.Amount > remainingDeposit)
            {
                _logger.LogWarning(
                    "User {UserId} amount {Amount} exceeds remaining deposit cap {Remaining}",
                    request.UserId,
                    request.Amount,
                    remainingDeposit
                );
                throw new BadRequestException(
                    $"مبلغ واریزی بیش از سقف باقیمانده است. حداکثر مبلغ قابل واریز: {remainingDeposit:N0} تومان"
                );
            }

            // 5. ایجاد درخواست پرداخت — callback مستقیم به فرانت‌آفیس
            var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268";
            var callbackUrl = $"{frontOfficeBaseUrl}/profile/payment-callback?type=magic-wallet";

            var paymentRequest = new PaymentRequest
            {
                Amount = request.Amount,
                UserId = user.Id,
                Mobile = user.Mobile ?? "",
                CallbackUrl = callbackUrl,
                Description = $"شارژ کیف‌پول جادویی - کاربر {user.Id}"
            };

            var paymentResult = await _paymentGateway.InitiatePaymentAsync(paymentRequest);

            if (!paymentResult.IsSuccess)
            {
                _logger.LogError(
                    "Payment gateway failed for magic charge UserId {UserId}: {ErrorMessage}",
                    user.Id,
                    paymentResult.ErrorMessage
                );

                throw new Exception($"خطا در ارتباط با درگاه پرداخت: {paymentResult.ErrorMessage}");
            }

            // 6. ثبت PaymentTransaction
            var paymentTx = new PaymentTransaction
            {
                GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal",
                MerchantId = _configuration["ZarinPal:MerchantId"] ?? "",
                Amount = request.Amount,
                CallbackUrl = callbackUrl,
                Description = $"شارژ کیف‌پول جادویی - کاربر {user.Id}",
                Mobile = user.Mobile,
                UserId = user.Id,
                RequestStatusCode = 100,
                RequestStatusMessage = "Success",
                Authority = paymentResult.RefId,
                PaymentStatus = false
            };

            _context.PaymentTransactions.Add(paymentTx);
            await _context.SaveChangesAsync(cancellationToken);

            _logger.LogInformation(
                "Magic wallet charge initiated. UserId: {UserId}, Amount: {Amount}, " +
                "RemainingCap: {Remaining}, RefId: {RefId}, PaymentTxId: {PaymentTxId}",
                user.Id,
                request.Amount,
                remainingDeposit - request.Amount,
                paymentResult.RefId,
                paymentTx.Id
            );

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