<?php

namespace App\Services;

use App\Models\ScheduleValidatorModel;

class SchedulePreValidator
{
    protected ScheduleValidatorModel $validatorModel;

    public function __construct()
    {
        $this->validatorModel = new ScheduleValidatorModel();
    }

    /**
     * Validasi lengkap sebelum generate.
     * Menghasilkan errors (blocker) dan warnings (peringatan).
     */
    public function validate(int $scheduleId, int $yearId, int $periodId): array
    {
        $schedule = $this->validatorModel->db->table('schedules')
            ->where('id', $scheduleId)
            ->get()
            ->getRowArray();

        if (!$schedule) {
            return [
                'can_generate'  => false,
                'errors'        => [['code' => 'NOT_FOUND', 'message' => 'Schedule tidak ditemukan', 'detail' => '', 'action' => '', 'action_label' => '']],
                'warnings'      => [],
                'summary'       => [],
            ];
        }

        $activeDays  = array_map('intval', explode(',', $schedule['active_days']));
        $capacity    = $this->validatorModel->getTotalSlotsCapacity($scheduleId);
        $loads       = $this->validatorModel->getTotalLoad($yearId, $periodId);

        $errors   = [];
        $warnings = [];

        // ── 1. Slot KBM harus ada ────────────────────────────────────
        if ($capacity['total_slots'] == 0) {
            $errors[] = [
                'code'         => 'NO_SLOTS',
                'message'      => 'Tidak ada slot waktu KBM aktif.',
                'detail'       => 'Buka menu Slot Waktu dan tambahkan slot dengan tipe "KBM".',
                'action'       => '/schedule-timeslot',
                'action_label' => 'Buka Slot Waktu',
            ];
        }

        // ── 2. Kelas harus ada ───────────────────────────────────────
        if ($capacity['total_classrooms'] == 0) {
            $errors[] = [
                'code'         => 'NO_CLASSROOMS',
                'message'      => 'Belum ada data kelas.',
                'detail'       => 'Tambahkan kelas di menu Master Data > Kelas.',
                'action'       => '/classroom',
                'action_label' => 'Buka Data Kelas',
            ];
        }

        // ── 3. Beban mengajar harus ada ──────────────────────────────
        if ($loads['total_loads'] === 0) {
            $errors[] = [
                'code'         => 'NO_LOADS',
                'message'      => 'Belum ada beban mengajar untuk periode ini.',
                'detail'       => 'Input beban mengajar di menu Beban Mengajar terlebih dahulu.',
                'action'       => '/schedule-load',
                'action_label' => 'Buka Beban Mengajar',
            ];
        }

        // Stop di sini jika data dasar belum ada
        if (!empty($errors)) {
            return $this->buildResult(false, $errors, $warnings, $capacity, $loads, [], []);
        }

        $slotsPerClass = array_sum($capacity['slots_per_day']);

        // ── 4. Cek kapasitas global ──────────────────────────────────
        if ($loads['total_hours'] > $capacity['total_slots']) {
            $overload = $loads['total_hours'] - $capacity['total_slots'];
            $errors[] = [
                'code'         => 'GLOBAL_OVERLOAD',
                'message'      => "Total beban ({$loads['total_hours']} jam) melebihi kapasitas slot ({$capacity['total_slots']} jam).",
                'detail'       => "Kelebihan {$overload} jam. Kurangi beban mengajar atau tambah slot KBM.",
                'action'       => '/schedule-load',
                'action_label' => 'Kelola Beban',
            ];
        } elseif ($loads['total_hours'] > $capacity['total_slots'] * 0.92) {
            $pct = round($loads['total_hours'] / $capacity['total_slots'] * 100);
            $warnings[] = [
                'code'    => 'HIGH_CAPACITY',
                'message' => "Kapasitas sudah {$pct}% terisi.",
                'detail'  => "Jadwal sangat padat, generator mungkin tidak mencapai 100%. Pertimbangkan menambah slot atau hari aktif.",
                'action'  => '',
                'action_label' => '',
            ];
        }

        // ── 5. Cek kapasitas per kelas ───────────────────────────────
        $classOverload = [];
        foreach ($loads['per_classroom'] as $class) {
            if ($slotsPerClass > 0 && $class['total_hours'] > $slotsPerClass) {
                $classOverload[] = $class['classroom_name'] . " ({$class['total_hours']}/{$slotsPerClass} jam)";
            }
        }
        if (!empty($classOverload)) {
            $errors[] = [
                'code'         => 'CLASS_OVERLOAD',
                'message'      => count($classOverload) . ' kelas melebihi kapasitas slot.',
                'detail'       => 'Kelas bermasalah: ' . implode(', ', $classOverload) . '. Kurangi beban mapel di kelas tersebut.',
                'action'       => '/schedule-load',
                'action_label' => 'Kelola Beban',
            ];
        }

        // ── 6. Cek guru overload (total jam vs hari tersedia) ────────
        $teacherFeasibility = $this->validatorModel->getTeacherFeasibility($yearId, $periodId, $activeDays);
        $overloadedTeachers = array_filter($teacherFeasibility, fn($t) => !$t['is_feasible']);

        foreach ($overloadedTeachers as $t) {
            $warnings[] = [
                'code'    => 'TEACHER_OVERLOAD',
                'message' => "Guru {$t['teacher_name']}: {$t['total_hours']} jam/minggu, butuh min. {$t['min_days_needed']} hari.",
                'detail'  => "Dengan max {$t['max_per_day']} jam/hari dan {$t['available_days']} hari aktif, jadwal sangat ketat. " .
                             "Pertimbangkan menambah hari aktif atau naikkan max jam/hari di Pengaturan.",
                'action'  => '/schedule-availability',
                'action_label' => 'Cek Ketersediaan',
            ];
        }

        // ── 7. Cek guru diblokir terlalu banyak ─────────────────────
        $blockedTeachers = $this->getHeavilyBlockedTeachers($yearId, $periodId, $activeDays, $capacity['slots_per_day']);
        foreach ($blockedTeachers as $bt) {
            $errors[] = [
                'code'         => 'TEACHER_BLOCKED',
                'message'      => "Guru {$bt['teacher_name']}: diblokir terlalu banyak slot.",
                'detail'       => "Punya {$bt['total_hours']} jam beban, tapi hanya {$bt['available_slots']} slot tersedia " .
                                  "(setelah dikurangi blokir). Longgarkan ketersediaan guru ini.",
                'action'       => '/schedule-availability',
                'action_label' => 'Cek Ketersediaan',
            ];
        }

        // ── 8. Cek duplikasi beban (mapel+kelas sama, 2 guru) ────────
        $duplicates = $this->getDuplicateLoads($yearId, $periodId);
        foreach ($duplicates as $dup) {
            $warnings[] = [
                'code'    => 'DUPLICATE_LOAD',
                'message' => "Duplikasi: {$dup['subject_name']} di {$dup['classroom_name']} diajar 2 guru.",
                'detail'  => "Guru: {$dup['teachers']}. HC7 akan mempersulit penempatan. " .
                             "Pertimbangkan hanya 1 guru per mapel per kelas.",
                'action'  => '/schedule-load',
                'action_label' => 'Kelola Beban',
            ];
        }

        // ── 9. Cek slot KBM per hari cukup untuk kelas ───────────────
        $dayNames = ['','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'];
        foreach ($activeDays as $day) {
            $slotsDay = $capacity['slots_per_day'][$day] ?? 0;
            if ($slotsDay === 0) {
                $warnings[] = [
                    'code'         => 'NO_SLOTS_DAY',
                    'message'      => "Hari {$dayNames[$day]} tidak punya slot KBM aktif.",
                    'detail'       => "Generator tidak bisa menaruh jadwal di hari ini. Tambah slot di menu Slot Waktu.",
                    'action'       => '/schedule-timeslot',
                    'action_label' => 'Buka Slot Waktu',
                ];
            }
        }

        $canGenerate = empty($errors);

        return $this->buildResult($canGenerate, $errors, $warnings, $capacity, $loads, $teacherFeasibility, $blockedTeachers);
    }

    // ── HELPER: Guru yang diblokir hampir semua slot ─────────────────
    private function getHeavilyBlockedTeachers(int $yearId, int $periodId, array $activeDays, array $slotsPerDay): array
    {
        $db = $this->validatorModel->db;

        $loads = $db->table('schedule_loads')
            ->select('teacher_id, teachers.name as teacher_name, SUM(hours_per_week) as total_hours')
            ->join('teachers', 'teachers.id = schedule_loads.teacher_id')
            ->where('academic_year_id', $yearId)
            ->where('academic_period_id', $periodId)
            ->groupBy('teacher_id')
            ->get()->getResultArray();

        $totalSlotsPerDay = [];
        foreach ($activeDays as $day) {
            $totalSlotsPerDay[$day] = $slotsPerDay[$day] ?? 0;
        }

        $results = [];
        foreach ($loads as $load) {
            $tid = $load['teacher_id'];

            $blocked = $db->table('schedule_availability')
                ->whereIn('day', $activeDays)
                ->where('teacher_id', $tid)
                ->get()->getResultArray();

            $blockedPerDay = [];
            foreach ($blocked as $b) {
                $d = (int)$b['day'];
                $s = (int)$b['slot_number'];
                if ($s === 0) {
                    $blockedPerDay[$d] = $totalSlotsPerDay[$d] ?? 0;
                } else {
                    $blockedPerDay[$d] = ($blockedPerDay[$d] ?? 0) + 1;
                }
            }

            $availableSlots = 0;
            foreach ($activeDays as $day) {
                $total = $totalSlotsPerDay[$day] ?? 0;
                $blk   = $blockedPerDay[$day] ?? 0;
                $availableSlots += max(0, $total - $blk);
            }

            $needed = (int)$load['total_hours'];
            if ($needed > 0 && $availableSlots < $needed) {
                $results[] = [
                    'teacher_id'      => $tid,
                    'teacher_name'    => $load['teacher_name'],
                    'total_hours'     => $needed,
                    'available_slots' => $availableSlots,
                ];
            }
        }

        return $results;
    }

    // ── HELPER: Duplikasi beban (mapel+kelas sama, 2+ guru) ──────────
    private function getDuplicateLoads(int $yearId, int $periodId): array
    {
        $db = $this->validatorModel->db;

        $rows = $db->table('schedule_loads sl')
            ->select('sl.school_subject_id, sl.classroom_id, ss.name as subject_name, c.name as classroom_name, COUNT(*) as cnt, GROUP_CONCAT(t.name SEPARATOR ", ") as teachers')
            ->join('school_subjects ss', 'ss.id = sl.school_subject_id')
            ->join('classrooms c', 'c.id = sl.classroom_id')
            ->join('teachers t', 't.id = sl.teacher_id')
            ->where('sl.academic_year_id', $yearId)
            ->where('sl.academic_period_id', $periodId)
            ->groupBy('sl.school_subject_id, sl.classroom_id')
            ->having('cnt >', 1)
            ->get()->getResultArray();

        return $rows;
    }

    // ── BUILD RESULT ─────────────────────────────────────────────────
    private function buildResult(
        bool  $canGenerate,
        array $errors,
        array $warnings,
        array $capacity,
        array $loads,
        array $teacherFeasibility,
        array $blockedTeachers
    ): array {
        $slotsPerClass = array_sum($capacity['slots_per_day'] ?? []);

        return [
            'can_generate' => $canGenerate,
            'errors'       => $errors,
            'warnings'     => $warnings,
            'summary'      => [
                'total_slots'          => $capacity['total_slots'] ?? 0,
                'total_load_hours'     => $loads['total_hours'] ?? 0,
                'total_classrooms'     => $capacity['total_classrooms'] ?? 0,
                'slots_per_class'      => $slotsPerClass,
                'slots_per_day'        => $capacity['slots_per_day'] ?? [],
                'per_teacher'          => $loads['per_teacher'] ?? [],
                'per_classroom'        => $loads['per_classroom'] ?? [],
                'teacher_feasibility'  => $teacherFeasibility,
                'blocked_teachers'     => $blockedTeachers,
                'utilization_pct'      => ($capacity['total_slots'] ?? 0) > 0
                    ? round(($loads['total_hours'] ?? 0) / $capacity['total_slots'] * 100)
                    : 0,
            ],
        ];
    }
}
