using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.EntityFrameworkCore;

namespace CMSMicroservice.Infrastructure.Services.Commission;

/// <summary>
/// پیاده‌سازی محاسبه کمیسیون با استفاده از Stored Procedure
/// این روش برای داده‌های زیاد بهینه‌تر است و از CTE استفاده می‌کند
/// حلقه روی پکیج‌های فعال — هر پکیج با تنظیمات خودش
/// </summary>
public class StoredProcedureCommissionCalculationStrategy : ICommissionCalculationStrategy
{
    private readonly IApplicationDbContext _context;

    public StoredProcedureCommissionCalculationStrategy(IApplicationDbContext context)
    {
        _context = context;
    }

    /// <inheritdoc />
    public async Task<int> CalculateWeeklyBalancesAsync(
        long weekDefinitionId,
        bool forceRecalculate,
        long? packageId = null,
        CancellationToken cancellationToken = default)
    {
        var dbContext = _context as DbContext;
        if (dbContext == null)
        {
            throw new InvalidOperationException("DbContext برای اجرای Stored Procedure در دسترس نیست");
        }

        // بارگذاری پکیج‌های فعال — اگر packageId داده شده فقط همان پکیج
        var packagesQuery = _context.Packages.Where(p => !p.IsDeleted && p.IsActive);
        if (packageId.HasValue)
            packagesQuery = packagesQuery.Where(p => p.Id == packageId.Value);
        var activePackages = await packagesQuery.ToListAsync(cancellationToken);

        if (!activePackages.Any())
        {
            throw new InvalidOperationException("هیچ پکیج فعالی یافت نشد");
        }

        var totalRowCount = 0;

        var connection = dbContext.Database.GetDbConnection();
        await connection.OpenAsync(cancellationToken);

        try
        {
            // حلقه روی پکیج‌ها — هر پکیج با تنظیمات خودش
            foreach (var package in activePackages)
            {
                using var command = connection.CreateCommand();
                command.CommandText = "CMS.sp_CalculateWeeklyBalances";
                command.CommandType = System.Data.CommandType.StoredProcedure;
                command.CommandTimeout = 300; // 5 دقیقه timeout

                // پارامتر WeekDefinitionId
                var paramWeekDefinitionId = command.CreateParameter();
                paramWeekDefinitionId.ParameterName = "@WeekDefinitionId";
                paramWeekDefinitionId.Value = weekDefinitionId;
                command.Parameters.Add(paramWeekDefinitionId);

                // پارامتر ForceRecalculate
                var paramForceRecalculate = command.CreateParameter();
                paramForceRecalculate.ParameterName = "@ForceRecalculate";
                paramForceRecalculate.Value = forceRecalculate;
                command.Parameters.Add(paramForceRecalculate);

                // پارامتر PackageId — per-package
                var paramPackageId = command.CreateParameter();
                paramPackageId.ParameterName = "@PackageId";
                paramPackageId.Value = package.Id;
                command.Parameters.Add(paramPackageId);

                // پارامتر MaxBalancesPerLeg — per-package
                var paramMaxBalances = command.CreateParameter();
                paramMaxBalances.ParameterName = "@InputMaxBalancesPerLeg";
                paramMaxBalances.Value = package.MaxBalancesPerLeg;
                command.Parameters.Add(paramMaxBalances);

                // پارامتر MaxNetworkLevel — per-package
                var paramMaxLevel = command.CreateParameter();
                paramMaxLevel.ParameterName = "@InputMaxNetworkLevel";
                paramMaxLevel.Value = package.MaxNetworkLevel;
                command.Parameters.Add(paramMaxLevel);

                // پارامتر خروجی
                var paramRowCount = command.CreateParameter();
                paramRowCount.ParameterName = "@RowCount";
                paramRowCount.Direction = System.Data.ParameterDirection.Output;
                paramRowCount.DbType = System.Data.DbType.Int32;
                command.Parameters.Add(paramRowCount);

                await command.ExecuteNonQueryAsync(cancellationToken);

                var rowCount = (int)(paramRowCount.Value ?? 0);
                totalRowCount += rowCount;
            }

            return totalRowCount;
        }
        finally
        {
            await connection.CloseAsync();
        }
    }

    /// <inheritdoc />
    public async Task<long> CalculateWeeklyCommissionPoolAsync(
        long weekDefinitionId,
        bool forceRecalculate,
        long? packageId = null,
        CancellationToken cancellationToken = default)
    {
        var dbContext = _context as DbContext;
        if (dbContext == null)
        {
            throw new InvalidOperationException("DbContext برای اجرای Stored Procedure در دسترس نیست");
        }

        // بارگذاری پکیج‌های فعال — اگر packageId داده شده فقط همان پکیج
        var packagesQuery = _context.Packages.Where(p => !p.IsDeleted && p.IsActive);
        if (packageId.HasValue)
            packagesQuery = packagesQuery.Where(p => p.Id == packageId.Value);
        var activePackages = await packagesQuery.ToListAsync(cancellationToken);

        if (!activePackages.Any())
        {
            throw new InvalidOperationException("هیچ پکیج فعالی یافت نشد");
        }

        var connection = dbContext.Database.GetDbConnection();
        await connection.OpenAsync(cancellationToken);

        long lastPoolId = 0;

        try
        {
            // حلقه روی پکیج‌ها — هر پکیج Pool مستقل دارد
            foreach (var package in activePackages)
            {
                
                using var command = connection.CreateCommand();
                command.CommandText = "CMS.sp_CalculateWeeklyCommissionPool";
                command.CommandType = System.Data.CommandType.StoredProcedure;
                command.CommandTimeout = 300;

                var paramWeekDefinitionId = command.CreateParameter();
                paramWeekDefinitionId.ParameterName = "@WeekDefinitionId";
                paramWeekDefinitionId.Value = weekDefinitionId;
                command.Parameters.Add(paramWeekDefinitionId);

                var paramPackageId = command.CreateParameter();
                paramPackageId.ParameterName = "@PackageId";
                paramPackageId.Value = package.Id;
                command.Parameters.Add(paramPackageId);

                var paramForceRecalculate = command.CreateParameter();
                paramForceRecalculate.ParameterName = "@ForceRecalculate";
                paramForceRecalculate.Value = forceRecalculate;
                command.Parameters.Add(paramForceRecalculate);

                var paramPoolId = command.CreateParameter();
                paramPoolId.ParameterName = "@PoolId";
                paramPoolId.Direction = System.Data.ParameterDirection.Output;
                paramPoolId.DbType = System.Data.DbType.Int64;
                command.Parameters.Add(paramPoolId);

                await command.ExecuteNonQueryAsync(cancellationToken);

                lastPoolId = (long)(paramPoolId.Value ?? 0L);
            }

            return lastPoolId;
        }
        finally
        {
            await connection.CloseAsync();
        }
    }
}
