using CMSMicroservice.Domain.Common;

namespace CMSMicroservice.Application.CommissionCQ.Commands.ProcessUserPayouts;

public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayoutsCommand, int>
{
    private readonly IApplicationDbContext _context;
    private readonly IWeekDefinitionRepository _weekRepository;

    public ProcessUserPayoutsCommandHandler(
        IApplicationDbContext context,
        IWeekDefinitionRepository weekRepository)
    {
        _context = context;
        _weekRepository = weekRepository;
    }

    public async Task<int> Handle(ProcessUserPayoutsCommand request, CancellationToken cancellationToken)
    {
        var regorianWeekNumber = _weekRepository.GetGregorianWeekNumber(request.WeekDefinitionId);
        if (regorianWeekNumber == null)
        {
            throw new InvalidOperationException($"هفته {request.WeekDefinitionId} در سیستم تعریف نشده است");
        }
        
        // بررسی وجود استخر
        var pool = await _context.WeeklyCommissionPools
            .FirstOrDefaultAsync(x => x.WeekDefinitionId == request.WeekDefinitionId, cancellationToken);

        if (pool == null || !pool.IsCalculated)
        {
            throw new InvalidOperationException($"استخر کمیسیون هفته {request.WeekDefinitionId} هنوز محاسبه نشده است");
        }

        // بررسی پرداخت قبلی
        var existingPayouts = await _context.UserCommissionPayouts
            .Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
            .ToListAsync(cancellationToken);

        if (existingPayouts.Any() && !request.ForceReprocess)
        {
            throw new InvalidOperationException($"پرداخت‌های هفته {request.WeekDefinitionId} قبلاً انجام شده است");
        }

        // حذف پرداخت‌های قبلی در صورت ForceReprocess
        if (existingPayouts.Any())
        {
            _context.UserCommissionPayouts.RemoveRange(existingPayouts);
            await _context.SaveChangesAsync(cancellationToken);
        }

        // بارگذاری پکیج پایه برای خواندن سقف‌ها
        var package = await _context.Packages
            .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
            ?? throw new InvalidOperationException("پکیج پایه یافت نشد");
        var maxNetworkLevel = package.MaxNetworkLevel;

        // دریافت همه تعادل‌های هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه)
        var allWeeklyBalances = await _context.NetworkWeeklyBalances
            .Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
            .ToDictionaryAsync(x => x.UserId, cancellationToken);

        // دریافت کاربرانی که تعادل > 0 دارند (یا زیرمجموعه‌شان دارد)
        var usersWithBalances = await _context.NetworkWeeklyBalances
            .Where(x => x.WeekDefinitionId == request.WeekDefinitionId && x.TotalBalances > 0)
            .Select(x => x.UserId)
            .ToListAsync(cancellationToken);

        // پیدا کردن تمام کاربرانی که باید کمیسیون بگیرند (شامل والدین)
        var usersToProcess = new HashSet<long>(usersWithBalances);
        
        // اضافه کردن والدین تا 15 لول بالاتر
        foreach (var userId in usersWithBalances)
        {
            var ancestors = await GetAncestors(userId, maxNetworkLevel, cancellationToken);
            foreach (var ancestorId in ancestors)
            {
                usersToProcess.Add(ancestorId);
            }
        }

        var payoutsList = new List<UserCommissionPayout>();

        foreach (var userId in usersToProcess)
        {
            // ⭐ محاسبه تعادل شخصی
            var personalBalances = 0;
            if (allWeeklyBalances.ContainsKey(userId))
            {
                personalBalances = allWeeklyBalances[userId].TotalBalances;
            }

            // ⭐ محاسبه مجموع تعادل‌های زیرمجموعه تا maxNetworkLevel لول
            var subordinateBalances = await CalculateSubordinateBalancesAsync(
                userId, 
                request.WeekDefinitionId,
                allWeeklyBalances,
                maxNetworkLevel,
                cancellationToken
            );

            // ⭐ مجموع تعادل = شخصی + زیرمجموعه
            var totalBalancesWithSubordinates = personalBalances + subordinateBalances;

            // اگر مجموع تعادل صفر است، نیازی به ثبت نیست
            if (totalBalancesWithSubordinates <= 0)
            {
                continue;
            }

            // محاسبه مبلغ کمیسیون
            var totalAmount = (long)(totalBalancesWithSubordinates * pool.ValuePerBalance);

            var payout = new UserCommissionPayout
            {
                UserId = userId,
                WeekDefinitionId = request.WeekDefinitionId,
                WeeklyPoolId = pool.Id,
                PackageId = package.Id,
                BalancesEarned = totalBalancesWithSubordinates, // ⭐ شامل زیرمجموعه
                ValuePerBalance = pool.ValuePerBalance,
                TotalAmount = totalAmount,
                Status = CommissionPayoutStatus.Pending,
                PaidAt = null,
                WithdrawalMethod = null,
                IbanNumber = null,
                WithdrawnAt = null
            };

            payoutsList.Add(payout);
        }

        await _context.UserCommissionPayouts.AddRangeAsync(payoutsList, cancellationToken);
        await _context.SaveChangesAsync(cancellationToken);

        // ثبت تاریخچه برای هر پرداخت
        var historyList = new List<CommissionPayoutHistory>();
        foreach (var payout in payoutsList)
        {
            var history = new CommissionPayoutHistory
            {
                UserCommissionPayoutId = payout.Id,
                UserId = payout.UserId,
                WeekDefinitionId = request.WeekDefinitionId,
                AmountBefore = 0,
                AmountAfter = payout.TotalAmount,
                OldStatus = default(CommissionPayoutStatus),
                NewStatus = CommissionPayoutStatus.Pending,
                Action = CommissionPayoutAction.Created,
                PerformedBy = "System",
                Reason = "پردازش خودکار کمیسیون هفتگی"
            };

            historyList.Add(history);
        }

        await _context.CommissionPayoutHistories.AddRangeAsync(historyList, cancellationToken);
        await _context.SaveChangesAsync(cancellationToken);

        return payoutsList.Count;
    }

    /// <summary>
    /// پیدا کردن والدین یک کاربر تا N لول بالاتر
    /// </summary>
    private async Task<List<long>> GetAncestors(long userId, int maxLevels, CancellationToken cancellationToken)
    {
        var ancestors = new List<long>();
        var currentUserId = userId;

        for (int level = 0; level < maxLevels; level++)
        {
            var user = await _context.Users
                .Where(x => x.Id == currentUserId)
                .Select(x => x.NetworkParentId)
                .FirstOrDefaultAsync(cancellationToken);

            if (user == null || !user.HasValue)
            {
                break;
            }

            ancestors.Add(user.Value);
            currentUserId = user.Value;
        }

        return ancestors;
    }

    /// <summary>
    /// محاسبه مجموع تعادل‌های زیرمجموعه یک کاربر تا N لول پایین‌تر
    /// </summary>
    private async Task<int> CalculateSubordinateBalancesAsync(
        long userId,
        long WeekDefinitionId,
        Dictionary<long, NetworkWeeklyBalance> allBalances,
        int maxLevel,
        CancellationToken cancellationToken)
    {
        // پیدا کردن همه زیرمجموعه‌ها تا maxLevel لول
        var subordinates = await GetSubordinatesRecursive(userId, 1, maxLevel, cancellationToken);

        // جمع تعادل‌های آنها
        var totalSubordinateBalances = 0;
        foreach (var subordinateId in subordinates)
        {
            if (allBalances.ContainsKey(subordinateId))
            {
                totalSubordinateBalances += allBalances[subordinateId].TotalBalances;
            }
        }

        return totalSubordinateBalances;
    }

    /// <summary>
    /// پیدا کردن بازگشتی زیرمجموعه‌ها تا N لول
    /// </summary>
    private async Task<List<long>> GetSubordinatesRecursive(
        long userId,
        int currentLevel,
        int maxLevel,
        CancellationToken cancellationToken)
    {
        // محدودیت عمق
        if (currentLevel > maxLevel)
        {
            return new List<long>();
        }

        var result = new List<long>();

        // پیدا کردن فرزندان مستقیم
        var children = await _context.Users
            .Where(x => x.NetworkParentId == userId)
            .Select(x => x.Id)
            .ToListAsync(cancellationToken);

        result.AddRange(children);

        // بازگشت برای هر فرزند
        foreach (var childId in children)
        {
            var grandChildren = await GetSubordinatesRecursive(childId, currentLevel + 1, maxLevel, cancellationToken);
            result.AddRange(grandChildren);
        }

        return result;
    }
}
