<?php

namespace App\Services\Promotions\Placement;

class BalancedSectionPlacementService
{
    public const ALGORITHM = 'score_band_balanced_v1';

    public function assign(array $students, int $capacity): array
    {
        $total = count($students);
        if ($total === 0) {
            return [
                'section_count' => 0,
                'target_sizes' => [],
                'sections' => [],
                'assignments' => [],
            ];
        }

        $sectionCount = $total <= $capacity ? 1 : (int) ceil($total / $capacity);
        $targetSizes = $this->targetSizes($total, $sectionCount);
        $sections = [];
        for ($i = 1; $i <= $sectionCount; $i++) {
            $sections[$i] = [
                'section_index' => $i,
                'target_size' => $targetSizes[$i],
                'total' => 0,
                'bands' => ['A' => 0, 'B' => 0, 'C' => 0, 'D' => 0],
                'students' => [],
            ];
        }

        $grouped = ['A' => [], 'B' => [], 'C' => [], 'D' => []];
        foreach ($students as $student) {
            $band = $student['score_band'] ?? 'D';
            $grouped[$band][] = $student;
        }

        foreach (array_keys($grouped) as $band) {
            usort($grouped[$band], function (array $a, array $b): int {
                $scoreA = $a['final_score'] ?? $a['placement_score'] ?? -1;
                $scoreB = $b['final_score'] ?? $b['placement_score'] ?? -1;
                if ($scoreA !== $scoreB) {
                    return $scoreB <=> $scoreA;
                }
                $name = strcmp((string) ($a['student_name'] ?? ''), (string) ($b['student_name'] ?? ''));
                return $name !== 0 ? $name : ((int) $a['student_id'] <=> (int) $b['student_id']);
            });
        }

        $assignments = [];
        $order = 1;
        foreach (['A', 'B', 'C', 'D'] as $band) {
            foreach ($grouped[$band] as $student) {
                $sectionIndex = $this->selectSection($sections, $band);
                $sections[$sectionIndex]['total']++;
                $sections[$sectionIndex]['bands'][$band]++;
                $sections[$sectionIndex]['students'][] = (int) $student['student_id'];
                $student['planned_section_index'] = $sectionIndex;
                $student['assignment_order'] = $order++;
                $assignments[] = $student;
            }
        }

        return [
            'section_count' => $sectionCount,
            'target_sizes' => $targetSizes,
            'sections' => array_values($sections),
            'assignments' => $assignments,
        ];
    }

    private function targetSizes(int $total, int $sectionCount): array
    {
        $base = intdiv($total, $sectionCount);
        $remainder = $total % $sectionCount;
        $sizes = [];
        for ($i = 1; $i <= $sectionCount; $i++) {
            $sizes[$i] = $base + ($i <= $remainder ? 1 : 0);
        }
        return $sizes;
    }

    private function selectSection(array $sections, string $band): int
    {
        $eligible = array_filter($sections, fn ($s) => $s['total'] < $s['target_size']);
        if (empty($eligible)) {
            $eligible = $sections;
        }

        uasort($eligible, function (array $a, array $b) use ($band): int {
            $cmp = $a['total'] <=> $b['total'];
            if ($cmp !== 0) {
                return $cmp;
            }
            $cmp = ($a['bands'][$band] ?? 0) <=> ($b['bands'][$band] ?? 0);
            if ($cmp !== 0) {
                return $cmp;
            }
            return $a['section_index'] <=> $b['section_index'];
        });

        return (int) array_key_first($eligible);
    }
}
