$path = "NPP.SmartSchedue.Api/Services/Integration/Algorithms/LinearProgrammingEngine.cs"
$lines = [System.Collections.Generic.List[string]]::new()
$lines.AddRange([System.IO.File]::ReadAllLines($path))

function FindExactIndexInRange($lines, $value, $start, $end) {
    for ($i = $start; $i -le $end; $i++) {
        if ($lines[$i] -eq $value) { return $i }
    }
    return -1
}

function FindContainsIndexInRange($lines, $value, $start, $end) {
    for ($i = $start; $i -le $end; $i++) {
        if ($lines[$i].Contains($value)) { return $i }
    }
    return -1
}

# --- BuildAuxiliaryVariables ---
$index = $lines.IndexOf('            var auxiliaryVars = new Dictionary<string, Variable>();')
if ($index -ge 0) {
    $lines.InsertRange($index + 1, [string[]]@(
'            var historicalWorkloads = BuildHistoricalWorkloadMap(context, out var totalHistoricalTasks);',
'            var avgWorkload = context.AvailablePersonnel.Count > 0',
'                ? (double)(context.Tasks.Count + totalHistoricalTasks) / context.AvailablePersonnel.Count',
'                : 0.0;'
    ))
}
$index = $lines.IndexOf('            var avgWorkload = (double)context.Tasks.Count / context.AvailablePersonnel.Count;')
if ($index -ge 0) { $lines.RemoveAt($index) }
$index = $lines.IndexOf('                var negDeviationVar = solver.MakeNumVar(0, avgWorkload, $"neg_dev_{personnel.Id}");')
if ($index -ge 0) { $lines[$index] = '                var negDeviationVar = solver.MakeNumVar(0, Math.Max(0.0, avgWorkload), $"neg_dev_{personnel.Id}");' }

# --- AddFairnessConstraints ---
$start = $lines.IndexOf('        private void AddFairnessConstraints(')
if ($start -ge 0) {
    $brace = 0
    for ($end = $start; $end -lt $lines.Count; $end++) {
        if ($lines[$end].Contains('{')) { $brace++ }
        if ($lines[$end].Contains('}')) { $brace-- }
        if ($brace -eq 0) { break }
    }
    $lines.RemoveRange($start, $end - $start + 1)
    $newFairness = @(
'        private void AddFairnessConstraints(',
'            Solver solver,',
'            Dictionary<(long TaskId, long PersonnelId), Variable> decisionVars,',
'            Dictionary<string, Variable> auxiliaryVars,',
'            GlobalAllocationContext context)',
'        {',
'            var constraintCount = 0;',
'',
'            _logger.LogInformation("⚖️ 开始添加公平性约束...");',
'',
'            var totalTasks = context.Tasks.Count;',
'            var totalPersonnel = context.AvailablePersonnel.Count;',
'            var historicalWorkloads = BuildHistoricalWorkloadMap(context, out var totalHistoricalWorkload);',
'            var totalWorkload = totalTasks + totalHistoricalWorkload;',
'            var avgWorkload = totalPersonnel > 0 ? (double)totalWorkload / totalPersonnel : 0.0;',
'',
'            // 公平性约束1：负载均衡偏差计算',
'            foreach (var personnel in context.AvailablePersonnel)',
'            {',
'                var workloadVarKey = $"workload_{personnel.Id}";',
'                var posDevVarKey = $"pos_dev_{personnel.Id}";',
'                var negDevVarKey = $"neg_dev_{personnel.Id}";',
'                var historicalWorkload = historicalWorkloads.GetValueOrDefault(personnel.Id);',
'',
'                if (auxiliaryVars.ContainsKey(workloadVarKey) &&',
'                    auxiliaryVars.ContainsKey(posDevVarKey) &&',
'                    auxiliaryVars.ContainsKey(negDevVarKey))',
'                {',
'                    // 约束：currentWorkload + 历史工作量偏移 = avgWorkload + pos_deviation - neg_deviation',
'                    var targetWorkload = avgWorkload - historicalWorkload;',
'                    var balanceConstraint = solver.MakeConstraint(targetWorkload, targetWorkload,',
'                        $"load_balance_{personnel.Id}");',
'',
'                    balanceConstraint.SetCoefficient(auxiliaryVars[workloadVarKey], 1);',
'                    balanceConstraint.SetCoefficient(auxiliaryVars[posDevVarKey], -1);',
'                    balanceConstraint.SetCoefficient(auxiliaryVars[negDevVarKey], 1);',
'                    constraintCount++;',
'                }',
'            }',
'',
'            // 公平性约束2：最大负载限制 - 防止极端不均衡',
'            var maxTotalWorkload = Math.Ceiling(avgWorkload * 1.5);',
'            foreach (var personnel in context.AvailablePersonnel)',
'            {',
'                var workloadVarKey = $"workload_{personnel.Id}";',
'                var historicalWorkload = historicalWorkloads.GetValueOrDefault(personnel.Id);',
'                if (auxiliaryVars.ContainsKey(workloadVarKey))',
'                {',
'                    var constraint = solver.MakeConstraint(0, Math.Max(0.0, maxTotalWorkload - historicalWorkload),',
'                        $"max_workload_{personnel.Id}");',
'                    constraint.SetCoefficient(auxiliaryVars[workloadVarKey], 1);',
'                    constraintCount++;',
'                }',
'            }',
'',
'            // 公平性约束3：最小人员利用率约束 - 确保不会过度集中',
'            var minPersonnelUtilization = totalPersonnel == 0',
'                ? 0',
'                : (totalTasks > 10 ? Math.Max(3, Math.Ceiling(totalPersonnel * 0.4)) : Math.Min((double)totalPersonnel, 2));',
'',
'            // 创建人员参与指示变量',
'            var participationVars = new List<Variable>();',
'            foreach (var personnel in context.AvailablePersonnel)',
'            {',
'                var participationVar = solver.MakeBoolVar($"participation_{personnel.Id}");',
'                participationVars.Add(participationVar);',
'',
'                // 如果人员参与，则至少分配1个任务',
'                var constraint = solver.MakeConstraint(0, double.PositiveInfinity,',
'                    $"participation_min_{personnel.Id}");',
'',
'                constraint.SetCoefficient(participationVar, -1);',
'',
'                var personnelTasks = decisionVars.Where(v => v.Key.PersonnelId == personnel.Id);',
'                foreach (var kvp in personnelTasks)',
'                {',
'                    constraint.SetCoefficient(kvp.Value, 1);',
'                }',
'                constraintCount++;',
'            }',
'',
'            // 最小参与人数约束',
'            var minParticipationConstraint = solver.MakeConstraint(minPersonnelUtilization, double.PositiveInfinity,',
'                "min_personnel_utilization");',
'',
'            foreach (var participationVar in participationVars)',
'            {',
'                minParticipationConstraint.SetCoefficient(participationVar, 1);',
'            }',
'            constraintCount++;',
'',
'            _logger.LogDebug("✅ 公平性约束添加完成 - 约束数量：{ConstraintCount}，平均负载：{AvgWorkload:F2}，最大负载：{MaxWorkload:F2}，最小参与人数：{MinParticipation}",',
'                constraintCount, avgWorkload, maxTotalWorkload, minPersonnelUtilization);',
'        }'
    )
    $lines.InsertRange($start, $newFairness)
}

# Update call to LogWorkloadAnalysis
$index = $lines.IndexOf('                LogWorkloadAnalysis(workloadDistribution, context.Tasks.Count, context.AvailablePersonnel.Count);')
if ($index -ge 0) { $lines[$index] = '                LogWorkloadAnalysis(workloadDistribution, context.Tasks.Count, context.AvailablePersonnel.Count, context);' }

# ValidateSolutionQuality
$index = $lines.IndexOf('            var totalChecks = 0;')
if ($index -ge 0) { $lines.Insert($index + 1, '            var historicalWorkloads = BuildHistoricalWorkloadMap(context, out _);') }
$start = $lines.IndexOf('            // 检查3：负载均衡评估')
if ($start -ge 0) {
    $lines.RemoveRange($start, 8)
    $lines.InsertRange($start, [string[]]@(
'            // 检查3：负载均衡评估（纳入历史任务）',
'            var currentWorkloadMap = assignments',
'                .GroupBy(a => a.Value)',
'                .ToDictionary(g => g.Key, g => g.Count());',
'',
'            var combinedWorkloads = context.AvailablePersonnel.ToDictionary(',
'                p => p.Id,',
'                p => historicalWorkloads.GetValueOrDefault(p.Id) + currentWorkloadMap.GetValueOrDefault(p.Id));',
'',
'            if (combinedWorkloads.Any())',
'            {',
'                var workloadValues = combinedWorkloads.Values.Select(v => (decimal)v).ToList();',
'                var giniCoefficient = CalculateGiniCoefficient(workloadValues);',
'                metrics.LoadBalanceScore = Math.Max(0.0, 1.0 - giniCoefficient);',
'',
'                var utilizedCount = combinedWorkloads.Count(kv => kv.Value > 0);',
'                metrics.PersonnelUtilizationRate = context.AvailablePersonnel.Count > 0',
'                    ? (double)utilizedCount / context.AvailablePersonnel.Count',
'                    : 0.0;',
'            }'
    ))
}

# LogWorkloadAnalysis replacement
$start = $lines.IndexOf('        private void LogWorkloadAnalysis(Dictionary<long, decimal> workloadDistribution, int totalTasks, int totalPersonnel)')
if ($start -ge 0) {
    $brace = 0
    for ($end = $start; $end -lt $lines.Count; $end++) {
        if ($lines[$end].Contains('{')) { $brace++ }
        if ($lines[$end].Contains('}')) { $brace-- }
        if ($brace -eq 0) { break }
    }
    $lines.RemoveRange($start, $end - $start + 1)
    $newLog = @(
'        private void LogWorkloadAnalysis(',
'            Dictionary<long, decimal> workloadDistribution,',
'            int totalTasks,',
'            int totalPersonnel,',
'            GlobalAllocationContext context)',
'        {',
'            var historicalWorkloads = BuildHistoricalWorkloadMap(context, out _);',
'',
'            if (!workloadDistribution.Any() && historicalWorkloads.Values.All(value => value == 0))',
'            {',
'                return;',
'            }',
'',
'            var combinedWorkloads = context.AvailablePersonnel.ToDictionary(',
'                p => p.Id,',
'                p => workloadDistribution.GetValueOrDefault(p.Id) + historicalWorkloads.GetValueOrDefault(p.Id));',
'',
'            if (!combinedWorkloads.Any())',
'            {',
'                return;',
'            }',
'',
'            var combinedValues = combinedWorkloads.Values.Select(value => (decimal)value).ToList();',
'            var avgWorkload = combinedValues.Average(value => (double)value);',
'            var maxWorkload = combinedValues.Max();',
'            var minWorkload = combinedValues.Min();',
'            var utilizedCount = combinedWorkloads.Count(kv => kv.Value > 0);',
'            var utilizationRate = totalPersonnel > 0 ? (double)utilizedCount / totalPersonnel : 0.0;',
'',
'            _logger.LogInformation("📈 工作负载分析(含历史) - 平均：{Avg:F2}，最大：{Max}，最小：{Min}，当前批次任务数：{CurrentTasks}，人员利用率：{Util:P2}，基尼系数：{Gini:F4}",',
'                avgWorkload, maxWorkload, minWorkload, totalTasks, utilizationRate, CalculateGiniCoefficient(combinedValues));',
'',
'            var currentDistributionLog = string.Join(", ",',
'                workloadDistribution.OrderBy(kv => kv.Key)',
'                                    .Select(kv => $"人员{kv.Key}:{kv.Value}任务"));',
'            if (!string.IsNullOrWhiteSpace(currentDistributionLog))',
'            {',
'                _logger.LogDebug("📊 本次任务负载：{Distribution}", currentDistributionLog);',
'            }',
'',
'            var historicalDistributionLog = string.Join(", ",',
'                historicalWorkloads.OrderBy(kv => kv.Key)',
'                                   .Select(kv => $"人员{kv.Key}:{kv.Value}历史任务"));',
'            if (!string.IsNullOrWhiteSpace(historicalDistributionLog))',
'            {',
'                _logger.LogDebug("🕰️ 历史任务负载：{Distribution}", historicalDistributionLog);',
'            }',
'',
'            var cumulativeDistributionLog = string.Join(", ",',
'                combinedWorkloads.OrderBy(kv => kv.Key)',
'                                 .Select(kv => $"人员{kv.Key}:{kv.Value}累计任务"));',
'            _logger.LogDebug("⚖️ 累计任务负载：{Distribution}", cumulativeDistributionLog);',
'        }'
    )
    $lines.InsertRange($start, $newLog)
}

# Insert helper method after #region 辅助方法
$index = $lines.IndexOf('        #region 辅助方法')
if ($index -ge 0) {
    $lines.InsertRange($index + 1, [string[]]@(
'',
'        private Dictionary<long, int> BuildHistoricalWorkloadMap(GlobalAllocationContext context, out int totalHistoricalTasks)',
'        {',
'            var historicalWorkloads = new Dictionary<long, int>();',
'            totalHistoricalTasks = 0;',
'',
'            foreach (var personnel in context.AvailablePersonnel)',
'            {',
'                var historyCount = 0;',
'                if (context.PersonnelHistoryTasks.TryGetValue(personnel.Id, out var historyTasks) && historyTasks != null)',
'                {',
'                    historyCount = historyTasks.Count;',
'                }',
'',
'                historicalWorkloads[personnel.Id] = historyCount;',
'                totalHistoricalTasks += historyCount;',
'            }',
'',
'            return historicalWorkloads;',
'        }',
''
    ))
}

# CreateBatchIntegrityConstraints modifications
$start = $lines.IndexOf('        private int CreateBatchIntegrityConstraints(')
if ($start -ge 0) {
    $brace = 0
    for ($end = $start; $end -lt $lines.Count; $end++) {
        if ($lines[$end].Contains('{')) { $brace++ }
        if ($lines[$end].Contains('}')) { $brace-- }
        if ($brace -eq 0) { break }
    }

    $idx = FindExactIndexInRange $lines '            var totalPersonnel = context.AvailablePersonnel.Count;' $start $end
    if ($idx -ge 0) {
        $lines.InsertRange($idx + 1, [string[]]@(
'            var historicalWorkloads = BuildHistoricalWorkloadMap(context, out var totalHistoricalWorkload);',
'            var totalWorkload = totalTasks + totalHistoricalWorkload;',
'            var avgWorkload = totalPersonnel > 0 ? (double)totalWorkload / totalPersonnel : 0.0;'
        ))
        $orig = FindExactIndexInRange $lines '            var avgWorkload = (double)totalTasks / totalPersonnel;' $start ($end + 3)
        if ($orig -ge 0) { $lines.RemoveAt($orig) }
    }

    $afterToList = FindExactIndexInRange $lines '                    .ToList();' $start $end
    if ($afterToList -ge 0) { $lines.Insert($afterToList + 1, '                var historicalWorkload = historicalWorkloads.GetValueOrDefault(personnel.Id);') }

    $constraintLine = FindExactIndexInRange $lines '                    var constraint = solver.MakeConstraint(' $start $end
    if ($constraintLine -ge 0) {
        $lines.RemoveRange($constraintLine, 3)
        $lines.InsertRange($constraintLine, [string[]]@(
'                    var lowerBound = Math.Max(0.0, (avgWorkload - maxDeviation) - historicalWorkload);',
'                    var upperBound = Math.Max(lowerBound, (avgWorkload + maxDeviation) - historicalWorkload);',
'                    var constraint = solver.MakeConstraint(',
'                        lowerBound,',
'                        upperBound,',
'                        $"workload_balance_{personnel.Id}");'
        ))
    }
}

[System.IO.File]::WriteAllLines($path, $lines)
