<?php

namespace App\Services\Students;

use App\Models\ClassSection;
use App\Models\PromotionQueue;
use App\Models\StudentClass;
use Illuminate\Support\Facades\DB;

class StudentAutoDistributionService
{
    public function __construct(private StudentConfigService $configService)
    {
    }

    public function promotionTotals(?string $schoolYear = null): array
    {
        $context = $this->configService->context();
        $year = $schoolYear ?: (string) ($context['school_year'] ?? '');

        $bases = ClassSection::query()
            ->whereRaw("class_section_name NOT LIKE '%-%'")
            ->orderBy('class_id')
            ->get()
            ->toArray();

        $wanted = [];
        foreach ($bases as $row) {
            $nameRaw = (string) ($row['class_section_name'] ?? '');
            $name = strtolower($nameRaw);
            if ($name === 'kg' || $name === 'youth') {
                $wanted[] = $row;
                continue;
            }
            if (ctype_digit($name)) {
                $num = (int) $name;
                if ($num >= 1 && $num <= 9) {
                    $wanted[] = $row;
                }
            }
        }

        $rows = [];
        foreach ($wanted as $row) {
            $classId = (int) ($row['class_id'] ?? 0);
            if ($classId <= 0) {
                continue;
            }

            $cands = DB::table('promotion_queue as pq')
                ->select('pq.student_id')
                ->join('enrollments as e', function ($join) use ($year) {
                    $join->on('e.student_id', '=', 'pq.student_id')
                        ->where('e.school_year', '=', $year);
                })
                ->where('pq.to_class_id', $classId)
                ->where('pq.school_year_to', $year)
                ->whereIn('pq.status', ['queued', 'assigned', 'applied'])
                ->whereIn('e.enrollment_status', ['payment pending', 'enrolled'])
                ->groupBy('pq.student_id')
                ->get();

            $rows[] = [
                'class_id' => $classId,
                'class_section_id' => (int) ($row['class_section_id'] ?? 0),
                'class_section_name' => (string) ($row['class_section_name'] ?? ''),
                'total' => $cands->count(),
            ];
        }

        return [
            'year' => $year,
            'rows' => $rows,
        ];
    }

    public function autoDistribute(int $classId, int $studentsPerSection, ?string $schoolYear = null, ?int $classSectionId = null, ?int $userId = null): array
    {
        $context = $this->configService->context();
        $year = $schoolYear ?: (string) ($context['school_year'] ?? '');
        $semester = (string) ($context['semester'] ?? '');

        if ($classId <= 0 && $classSectionId > 0) {
            $classId = (int) (ClassSection::getClassId($classSectionId) ?? 0);
        }

        if ($classId <= 0 || $studentsPerSection <= 0) {
            return ['ok' => false, 'message' => 'Invalid class_id or students_per_section.'];
        }

        $cands = DB::table('promotion_queue as pq')
            ->select('pq.*', 'students.gender')
            ->join('students', 'students.id', '=', 'pq.student_id')
            ->where('pq.to_class_id', $classId)
            ->where('pq.school_year_to', $year)
            ->whereIn('pq.status', ['queued', 'assigned'])
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();

        if (empty($cands)) {
            return ['ok' => false, 'message' => 'No students found in promotion queue for selected class/year.'];
        }

        $studentIds = array_map(static fn ($r) => (int) $r['student_id'], $cands);
        $enrolledIds = DB::table('enrollments')
            ->select('student_id')
            ->whereIn('student_id', $studentIds)
            ->where('school_year', $year)
            ->whereIn('enrollment_status', ['payment pending', 'enrolled'])
            ->groupBy('student_id')
            ->pluck('student_id')
            ->map(fn ($v) => (int) $v)
            ->all();

        $cands = array_values(array_filter($cands, static function ($row) use ($enrolledIds) {
            return in_array((int) $row['student_id'], $enrolledIds, true);
        }));

        if (empty($cands)) {
            return ['ok' => false, 'message' => 'No eligible enrolled students found to distribute.'];
        }

        $sectionsNeeded = (int) ceil(count($cands) / $studentsPerSection);
        $letters = ClassSection::getLetterSectionsByClassId($classId);

        if (empty($letters)) {
            return ['ok' => false, 'message' => 'No lettered sections found for the selected class.'];
        }

        if (count($letters) < $sectionsNeeded) {
            return ['ok' => false, 'message' => 'Not enough sections available.'];
        }

        $letters = array_slice($letters, 0, $sectionsNeeded);
        $buckets = [];
        foreach ($letters as $idx => $section) {
            $buckets[$idx] = [
                'class_section_id' => (int) ($section['class_section_id'] ?? 0),
                'assigned' => [],
                'male' => 0,
                'female' => 0,
            ];
        }

        $males = [];
        $females = [];
        foreach ($cands as $row) {
            $gender = (string) ($row['gender'] ?? '');
            if (strcasecmp($gender, 'Female') === 0) {
                $females[] = $row;
            } else {
                $males[] = $row;
            }
        }

        $pickBucket = function (string $gender) use (&$buckets, $studentsPerSection): ?int {
            $bestIdx = null;
            $bestCnt = PHP_INT_MAX;
            foreach ($buckets as $idx => $bucket) {
                if (count($bucket['assigned']) >= $studentsPerSection) {
                    continue;
                }
                $cnt = $gender === 'female' ? $bucket['female'] : $bucket['male'];
                if ($cnt < $bestCnt) {
                    $bestCnt = $cnt;
                    $bestIdx = $idx;
                }
            }
            return $bestIdx;
        };

        foreach ($males as $row) {
            $idx = $pickBucket('male');
            if ($idx === null) {
                break;
            }
            $buckets[$idx]['assigned'][] = (int) $row['student_id'];
            $buckets[$idx]['male']++;
        }

        foreach ($females as $row) {
            $idx = $pickBucket('female');
            if ($idx === null) {
                break;
            }
            $buckets[$idx]['assigned'][] = (int) $row['student_id'];
            $buckets[$idx]['female']++;
        }

        $promoIdsByStudent = [];
        foreach ($cands as $row) {
            $promoIdsByStudent[(int) $row['student_id']] = (int) $row['id'];
        }

        DB::transaction(function () use ($buckets, $promoIdsByStudent, $year, $semester, $userId): void {
            foreach ($buckets as $bucket) {
                $sectionId = (int) $bucket['class_section_id'];
                foreach ($bucket['assigned'] as $studentId) {
                    if (isset($promoIdsByStudent[$studentId])) {
                        PromotionQueue::query()->where('id', $promoIdsByStudent[$studentId])->update([
                            'to_class_section_id' => $sectionId,
                            'status' => 'assigned',
                            'updated_by' => $userId,
                            'updated_at' => now(),
                        ]);
                    }

                    $exists = StudentClass::query()
                        ->where('student_id', $studentId)
                        ->where('school_year', $year)
                        ->when($semester !== '', fn ($q) => $q->where('semester', $semester))
                        ->first();

                    $payload = [
                        'student_id' => $studentId,
                        'class_section_id' => $sectionId,
                        'school_year' => $year,
                        'semester' => $semester,
                        'updated_by' => $userId,
                        'updated_at' => now(),
                    ];

                    if ($exists) {
                        $exists->update($payload);
                    } else {
                        StudentClass::query()->create($payload + ['created_at' => now()]);
                    }
                }
            }
        });

        $nameById = [];
        foreach ($letters as $row) {
            $nameById[(int) $row['class_section_id']] = (string) ($row['class_section_name'] ?? '');
        }

        $summary = [];
        foreach ($buckets as $bucket) {
            $sectionId = (int) $bucket['class_section_id'];
            $summary[] = [
                'class_section_id' => $sectionId,
                'class_section_name' => $nameById[$sectionId] ?? (string) $sectionId,
                'total' => count($bucket['assigned']),
                'male' => $bucket['male'],
                'female' => $bucket['female'],
            ];
        }

        return [
            'ok' => true,
            'message' => 'Auto distribution completed.',
            'sections' => $summary,
        ];
    }
}
