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

namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment;

/// <summary>
/// Handler برای ثبت اولیه تراکنش و سفارش پکیج پایه
/// این Handler فقط Transaction و Order را با وضعیت Pending ایجاد می‌کند
/// BFF سپس کاربر را به درگاه PYMS هدایت می‌کند
/// </summary>
public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<InitiateBasePackagePaymentCommand, InitiateBasePackagePaymentResponseDto>
{
    private readonly IApplicationDbContext _context;
    private readonly ILogger<InitiateBasePackagePaymentCommandHandler> _logger;
    
    /// <summary>
    /// شناسه پکیج پایه در دیتابیس
    /// </summary>
    private const long BasePackageId = 4;

    public InitiateBasePackagePaymentCommandHandler(
        IApplicationDbContext context,
        ILogger<InitiateBasePackagePaymentCommandHandler> logger)
    {
        _context = context;
        _logger = logger;
    }

    public async Task<InitiateBasePackagePaymentResponseDto> Handle(
        InitiateBasePackagePaymentCommand request, 
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Initiating base package payment for UserId: {UserId}",
                request.UserId);

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

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

            // 2. بررسی عدم خرید قبلی پکیج پایه
            if (user.PackagePurchaseMethod != PackagePurchaseMethod.None)
            {
                _logger.LogWarning(
                    "User {UserId} has already purchased base package via {Method}",
                    request.UserId,
                    user.PackagePurchaseMethod);

                return new InitiateBasePackagePaymentResponseDto
                {
                    Success = false,
                    Message = "شما قبلاً پکیج پایه را خریداری کرده‌اید."
                };
            }

            // 3. بررسی عدم وجود سفارش Pending
            var pendingOrder = await _context.UserOrders
                .FirstOrDefaultAsync(o => 
                    o.UserId == request.UserId && 
                    o.PaymentStatus == PaymentStatus.Pending &&
                    o.PackageId == BasePackageId, 
                    cancellationToken);

            if (pendingOrder != null)
            {
                _logger.LogWarning(
                    "User {UserId} has pending order {OrderId}",
                    request.UserId,
                    pendingOrder.Id);

                // برگرداندن سفارش موجود برای ادامه پرداخت
                var existingTransaction = await _context.Transactions
                    .FirstOrDefaultAsync(t => t.Id == pendingOrder.TransactionId, cancellationToken);

                return new InitiateBasePackagePaymentResponseDto
                {
                    Success = true,
                    Message = "سفارش قبلی در انتظار پرداخت یافت شد.",
                    OrderId = pendingOrder.Id,
                    TransactionId = existingTransaction?.Id ?? 0,
                    Amount = SystemConstants.BasePackageAmount
                };
            }

            // 4. پیدا کردن آدرس پیش‌فرض کاربر
            var defaultAddress = await _context.UserAddresses
                .Where(a => a.UserId == request.UserId)
                .OrderByDescending(a => a.Created)
                .FirstOrDefaultAsync(cancellationToken);

            if (defaultAddress == null)
            {
                _logger.LogWarning("No address found for user {UserId}", request.UserId);
                throw new ValidationException("لطفاً ابتدا یک آدرس برای خود ثبت کنید.");
            }

            // 5. ایجاد Transaction با وضعیت Pending
            var transaction = new Transaction
            {
                Amount = SystemConstants.BasePackageAmount,
                Description = $"خرید پکیج پایه ۵۶ میلیونی - کاربر #{user.Id}",
                PaymentStatus = PaymentStatus.Pending,
                Type = TransactionType.DepositIpg
            };

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

            _logger.LogInformation(
                "Created Transaction {TransactionId} with Pending status for UserId {UserId}",
                transaction.Id,
                request.UserId);

            // 6. ایجاد UserOrder با وضعیت Pending
            var order = new UserOrder
            {
                UserId = user.Id,
                PackageId = BasePackageId,
                Amount = SystemConstants.BasePackageAmount,
                PaymentStatus = PaymentStatus.Pending,
                DeliveryStatus = DeliveryStatus.None,
                UserAddressId = defaultAddress.Id,
                PaymentMethod = PaymentMethod.IPG,
                TransactionId = transaction.Id
            };

            _context.UserOrders.Add(order);
            await _context.SaveChangesAsync(cancellationToken);

            _logger.LogInformation(
                "Created UserOrder {OrderId} for UserId {UserId}, TransactionId: {TransactionId}",
                order.Id,
                request.UserId,
                transaction.Id);

            return new InitiateBasePackagePaymentResponseDto
            {
                Success = true,
                Message = "تراکنش و سفارش با موفقیت ثبت شد.",
                OrderId = order.Id,
                TransactionId = transaction.Id,
                Amount = SystemConstants.BasePackageAmount
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(
                ex,
                "Error in InitiateBasePackagePaymentCommand for UserId: {UserId}",
                request.UserId);
            throw;
        }
    }
}
