using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Application.Common.Helpers;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.Common.Models;
using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Entities.Club;
using CMSMicroservice.Domain.Entities.Commission;
using CMSMicroservice.Domain.Entities.History;
using CMSMicroservice.Domain.Enums;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Globalization;

namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership;

public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClubMembershipCommand, bool>
{
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUser;
    private readonly IWeekDefinitionRepository _weekRepository;
    private readonly ILogger<ActivateClubMembershipCommandHandler> _logger;

    public ActivateClubMembershipCommandHandler(
        IApplicationDbContext context,
        ICurrentUserService currentUser,
        IWeekDefinitionRepository weekRepository,
        ILogger<ActivateClubMembershipCommandHandler> logger)
    {
        _context = context;
        _currentUser = currentUser;
        _weekRepository = weekRepository;
        _logger = logger;
    }

    public async Task<bool> Handle(
        ActivateClubMembershipCommand request, 
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Activating club membership 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}", request.UserId);
                throw new NotFoundException(nameof(User), request.UserId);
            }

            // متغیر پکیج — در هر دو مسیر (عادی و Force) مقداردهی می‌شود
            Package package;

            // 2-5: بررسی‌های مالی — در حالت ForceActivation (ادمین) رد می‌شود
            if (!request.ForceActivation)
            {
                // 2. بررسی اینکه پکیج خریده باشد
                if (user.PackagePurchaseMethod == PackagePurchaseMethod.None)
                {
                    _logger.LogWarning(
                        "User {UserId} has not purchased any package yet",
                        request.UserId
                    );
                    throw new BadRequestException(
                        "برای فعالسازی باشگاه مشتریان ابتدا باید یک پکیج خریداری کنید"
                    );
                }

                // 3. بررسی موجودی کیف پول
                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("کیف پول کاربر یافت نشد");
                }

                // NOTE: balance check uses package.Price — loaded after finding order (step 4)
                // Moved to after package loading

                // 3.5. بررسی وضعیت کیف‌پول جادویی — اگر در حالت Magic است، فعالسازی مجاز نیست
                if (wallet.WalletMode == WalletMode.Magic)
                {
                    _logger.LogWarning(
                        "User {UserId} cannot activate club while in Magic wallet mode",
                        request.UserId
                    );
                    throw new BadRequestException(
                        "ابتدا باید چرخه کیف‌پول جادویی تکمیل شود"
                    );
                }

                // 4. پیدا کردن آخرین خرید موفق پکیج
                var packagePurchase = await _context.UserPackagePurchases
                    .Include(p => p.Transaction)
                    .Where(p =>
                        p.UserId == user.Id &&
                        p.Transaction != null &&
                        p.Transaction.PaymentStatus == PaymentStatus.Success)
                    .OrderByDescending(p => p.Created)
                    .FirstOrDefaultAsync(cancellationToken);

                if (packagePurchase == null)
                {
                    _logger.LogWarning(
                        "No successful package purchase found for UserId: {UserId}",
                        request.UserId
                    );
                    throw new NotFoundException("سفارش پکیج یافت نشد");
                }

                // 5. بررسی Transaction
                if (packagePurchase.Transaction == null)
                {
                    _logger.LogError(
                        "Transaction not found for PurchaseId: {PurchaseId}",
                        packagePurchase.Id
                    );
                    throw new NotFoundException("تراکنش مربوط به سفارش یافت نشد");
                }

                var transaction = packagePurchase.Transaction;

                if (transaction.Type != TransactionType.Buy &&
                    transaction.Type != TransactionType.DepositIpg &&
                    transaction.Type != TransactionType.DepositExternal1)
                {
                    _logger.LogWarning(
                        "Invalid transaction type for PurchaseId {PurchaseId}: {Type}",
                        packagePurchase.Id,
                        transaction.Type
                    );
                    throw new BadRequestException(
                        "تراکنش معتبر برای فعالسازی باشگاه یافت نشد"
                    );
                }

                // 5.5. بارگذاری پکیج از خرید
                package = await _context.Packages
                    .FirstOrDefaultAsync(p => p.Id == packagePurchase.PackageId && !p.IsDeleted, cancellationToken)
                    ?? throw new NotFoundException("پکیج یافت نشد");

                // بررسی موجودی با مبلغ پکیج واقعی
                if (wallet.Balance < package.Price)
                {
                    throw new BadRequestException(
                        $"برای فعالسازی باشگاه مشتریان باید حداقل {package.Price:N0} تومان موجودی اصلی داشته باشید"
                    );
                }
            }
            else
            {
                _logger.LogInformation(
                    "Force activation by admin for UserId: {UserId} — skipping order/wallet validation",
                    request.UserId
                );

                // در فعال‌سازی اجباری ادمین، اگر روش خرید تنظیم نشده، دستی در نظر بگیر
                if (user.PackagePurchaseMethod == PackagePurchaseMethod.None)
                {
                    user.PackagePurchaseMethod = PackagePurchaseMethod.Manual;
                    _context.Users.Update(user);
                    await _context.SaveChangesAsync(cancellationToken);
                }

                // بارگذاری پکیج — اگر PackageId مشخص شده همان را لود کن، وگرنه پکیج پایه
                if (request.PackageId > 0)
                {
                    package = await _context.Packages
                        .FirstOrDefaultAsync(p => p.Id == request.PackageId && !p.IsDeleted, cancellationToken)
                        ?? throw new NotFoundException($"پکیج {request.PackageId} یافت نشد");
                }
                else
                {
                    package = await _context.Packages
                        .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
                        ?? throw new NotFoundException("پکیج پایه یافت نشد");
                }
            }

            // 6. بررسی عضویت فعلی
            var existingMembership = await _context.ClubMemberships
                .FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken);

            // 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از پکیج
            long giftValue = package.ActivationFee;
            long activationFeeValue = package.ActivationFee;
            
            _logger.LogInformation(
                "Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}, Price={Price}",
                package.Id, package.Title, giftValue, activationFeeValue, package.Price
            );

            ClubMembership entity;
            bool isNewMembership = existingMembership == null;
            var activationDate = DateTime.Now;

            if (isNewMembership)
            {
                // ایجاد عضویت جدید — IsActive = false تا زمان امضای قرارداد باشگاه
                entity = new ClubMembership
                {
                    UserId = user.Id,
                    IsActive = false,
                    ActivatedAt = activationDate,
                    FirstActivationDate = activationDate,
                    FirstPackageId = package.Id,
                    LastActivationDate = activationDate,
                    LastPackageId = package.Id,
                    InitialContribution = activationFeeValue,
                    GiftValue = giftValue,
                    TotalEarned = 0,
                    PurchaseMethod = user.PackagePurchaseMethod
                };

                _context.ClubMemberships.Add(entity);
                
                _logger.LogInformation(
                    "Created new club membership for UserId {UserId} via {Method}, GiftValue: {GiftValue}",
                    user.Id,
                    user.PackagePurchaseMethod,
                    giftValue
                );
            }
            else
            {
                // بررسی آیا چرخه فعال دارد — اگر بله، واقعاً فعال است و نیازی به کار نیست
                var hasCurrentCycle = await _context.ClubMembershipCycles
                    .AnyAsync(c => c.UserId == user.Id && c.IsCurrentCycle, cancellationToken);

                // اگر چرخه فعال دارد و درخواست Force نیست — احتمال اجرای تکراری همان خرید
                // در حالت ForceActivation (مثل پرداخت دستی)، باید چرخه جدید ساخته شود تا پکیج جدید ثبت شود.
                if (hasCurrentCycle && !request.ForceActivation)
                {
                    _logger.LogInformation(
                        "User {UserId} already has a running cycle — skipping duplicate activation",
                        user.Id
                    );
                    return true;
                }

                // فعال‌سازی مجدد یا خرید مجدد (Q19/Q20) — FirstActivation حفظ میشه (Q21), Last بروزرسانی میشه
                // مقدار IsActive قبلی حفظ میشه — اگر قبلاً فعال بوده، فعال بمونه
                entity = existingMembership;
                entity.LastActivationDate = activationDate;
                entity.LastPackageId = package.Id;
                entity.PurchaseMethod = user.PackagePurchaseMethod;

                _context.ClubMemberships.Update(entity);
                
                _logger.LogInformation(
                    "Re-activating club membership for UserId {UserId} (re-purchase scenario, IsActive was {WasActive})",
                    user.Id,
                    existingMembership.IsActive
                );
            }

            await _context.SaveChangesAsync(cancellationToken);

            // 6.5. ایجاد ClubMembershipCycle — هر خرید پکیج یک دور جدید
            var previousCycles = await _context.ClubMembershipCycles
                .Where(c => c.UserId == user.Id && c.IsCurrentCycle)
                .ToListAsync(cancellationToken);

            foreach (var prevCycle in previousCycles)
            {
                prevCycle.IsCurrentCycle = false;
                ClubMembershipCycleHistoryWriter.WriteCycleClosed(_context, prevCycle, reason: "شروع دوره جدید");
            }

            var maxCycleNumber = await _context.ClubMembershipCycles
                .Where(c => c.ClubMembershipId == entity.Id)
                .MaxAsync(c => (int?)c.CycleNumber, cancellationToken) ?? 0;

            var newCycle = new ClubMembershipCycle
            {
                UserId = user.Id,
                ClubMembershipId = entity.Id,
                CycleNumber = maxCycleNumber + 1,
                PackagePurchasedAt = activationDate,
                PurchaseMethod = user.PackagePurchaseMethod,
                PackageAmount = package.Price,
                PackageId = package.Id,
                IsCurrentCycle = true
            };

            _context.ClubMembershipCycles.Add(newCycle);
            await _context.SaveChangesAsync(cancellationToken);

            ClubMembershipCycleHistoryWriter.WriteCycleStarted(_context, newCycle, reason: "فعال‌سازی عضویت باشگاه");
            await _context.SaveChangesAsync(cancellationToken);

            _logger.LogInformation(
                "Created ClubMembershipCycle #{CycleNumber} for UserId {UserId}, MembershipId {MembershipId}",
                newCycle.CycleNumber,
                user.Id,
                entity.Id
            );

            // 7. ثبت تاریخچه
            var history = new ClubMembershipHistory
            {
                ClubMembershipId = entity.Id,
                UserId = entity.UserId,
                OldIsActive = !isNewMembership && !existingMembership!.IsActive,
                NewIsActive = true,
                Action = ClubMembershipAction.Activated,
                Reason = isNewMembership
                    ? $"Initial activation via {user.PackagePurchaseMethod}"
                    : $"Reactivated via {user.PackagePurchaseMethod}",
                PerformedBy = _currentUser.GetPerformedBy()
            };

            _context.ClubMembershipHistories.Add(history);
            await _context.SaveChangesAsync(cancellationToken);

            // ⭐ 8. اضافه کردن مبلغ به Pool هفته جاری
            // عضو جدید (isNewMembership=true): Pool توسط AcceptClubMembershipContract شارژ می‌شود
            //   چون کاربر قبل از امضای قرارداد IsActive=false است و قرارداد فقط یک‌بار امضا می‌شود.
            // خرید مجدد (isNewMembership=false): قرارداد مجدد امضا نمی‌شود — Pool همین‌جا شارژ می‌شود.
            if (!isNewMembership)
            {
                var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
                var weeklyPool = await _context.WeeklyCommissionPools
                    .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken);

                if (weeklyPool == null)
                {
                    // ایجاد Pool جدید برای این هفته × این پکیج
                    weeklyPool = new WeeklyCommissionPool
                    {
                        WeekDefinitionId = currentWeekDefinitionId,
                        PackageId = package.Id,
                        TotalPoolAmount = activationFeeValue,
                        TotalBalances = 0,
                        ValuePerBalance = 0,
                        IsCalculated = false,
                        CalculatedAt = null
                    };

                    await _context.WeeklyCommissionPools.AddAsync(weeklyPool, cancellationToken);

                    _logger.LogInformation(
                        "Created new WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId} with initial amount: {Amount}",
                        currentWeekDefinitionId,
                        activationFeeValue
                    );
                }
                else
                {
                    weeklyPool.TotalPoolAmount += activationFeeValue;
                    _context.WeeklyCommissionPools.Update(weeklyPool);

                    _logger.LogInformation(
                        "Added {Amount} to existing WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId}. New total: {NewTotal}",
                        activationFeeValue,
                        currentWeekDefinitionId,
                        weeklyPool.TotalPoolAmount
                    );
                }

                await _context.SaveChangesAsync(cancellationToken);
            }
            else
            {
                _logger.LogInformation(
                    "New member — Pool charge deferred to AcceptClubMembershipContract for UserId {UserId}",
                    user.Id
                );
            }

            // 9. اعمال ویژگی‌های پکیج — DIFF/تفاضل (Q20)
            await ApplyFeatureDiffAsync(user.Id, entity.Id, package.Id, isNewMembership, activationDate, cancellationToken);

            _logger.LogInformation(
                "Club membership activated successfully. UserId: {UserId}, MembershipId: {MembershipId}",
                user.Id,
                entity.Id
            );

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

    /// <summary>
    /// اعمال DIFF فیچرها بر اساس پکیج (Q20)
    /// عضویت جدید: همه فیچرهای پکیج اضافه میشه
    /// خرید مجدد: تفاضل فیچرهای فعلی و پکیج جدید
    /// </summary>
    private async Task ApplyFeatureDiffAsync(
        long userId, long membershipId, long packageId,
        bool isNewMembership, DateTime activationDate,
        CancellationToken cancellationToken)
    {
        // فیچرهای پکیج جدید
        var newFeatureIds = await _context.PackageFeatures
            .Where(pf => pf.PackageId == packageId && pf.IsIncluded && !pf.IsDeleted)
            .Select(pf => pf.ClubFeatureId)
            .ToListAsync(cancellationToken);

        if (!newFeatureIds.Any())
        {
            _logger.LogWarning(
                "No PackageFeatures found for PackageId {PackageId} — skipping feature assignment for UserId {UserId}",
                packageId, userId);
            return;
        }

        // فیچرهای فعال فعلی کاربر
        var currentFeatures = await _context.UserClubFeatures
            .Where(ucf => ucf.UserId == userId && ucf.IsActive)
            .ToListAsync(cancellationToken);

        var currentFeatureIds = currentFeatures.Select(f => f.ClubFeatureId).ToHashSet();

        // DIFF
        var toAdd = newFeatureIds.Where(id => !currentFeatureIds.Contains(id)).ToList();
        var toRemove = currentFeatures.Where(f => !newFeatureIds.Contains(f.ClubFeatureId)).ToList();

        // حذف فیچرهای قدیمی که پکیج جدید ندارد
        foreach (var feature in toRemove)
        {
            feature.IsActive = false;
            feature.Notes = $"حذف شده بابت تغییر پکیج (PackageId: {packageId})";
        }

        // اضافه فیچرهای جدید
        if (toAdd.Any())
        {
            var newUserFeatures = toAdd.Select(featureId => new UserClubFeature
            {
                UserId = userId,
                ClubMembershipId = membershipId,
                ClubFeatureId = featureId,
                GrantedAt = activationDate,
                IsActive = true,
                Notes = isNewMembership
                    ? "اعطا شده هنگام فعالسازی اولیه"
                    : $"اضافه شده بابت خرید مجدد پکیج (PackageId: {packageId})"
            }).ToList();

            _context.UserClubFeatures.AddRange(newUserFeatures);
        }

        await _context.SaveChangesAsync(cancellationToken);

        _logger.LogInformation(
            "Feature DIFF applied for UserId={UserId}: Added={Added}, Removed={Removed}, Kept={Kept}",
            userId, toAdd.Count, toRemove.Count,
            currentFeatureIds.Count - toRemove.Count);
    }

    /// <summary>
    /// دریافت شناسه تعریف هفته جاری
    /// </summary>
    private long GetCurrentWeekDefinitionId()
    {
        var week = _weekRepository.GetCurrentWeek();
        if (week == null)
        {
            throw new InvalidOperationException("هفته جاری در سیستم تعریف نشده است");
        }
        return week.Id;
    }
}
