<?php

namespace App\Controllers\Admin;

use App\Controllers\BaseController;
use App\Models\ScheduleModel;
use App\Models\ScheduleEntryModel;
use App\Models\AcademicYearModel;
use App\Models\AcademicPeriodModel;
use App\Models\ScheduleTimeslotModel;

class ManualSchedule extends BaseController
{
    protected ScheduleModel       $scheduleModel;
    protected ScheduleEntryModel  $entryModel;
    protected AcademicYearModel   $yearModel;
    protected AcademicPeriodModel $periodModel;
    protected \CodeIgniter\Database\BaseConnection $db;

    public function __construct()
    {
        $this->scheduleModel = new ScheduleModel();
        $this->entryModel    = new ScheduleEntryModel();
        $this->yearModel     = new AcademicYearModel();
        $this->periodModel   = new AcademicPeriodModel();
        $this->db            = \Config\Database::connect();
    }

    // ------------------------------------------------------------------
    // ROUTE UTAMA INPUT JADWAL MANUAL YANG AKTIF
    // ------------------------------------------------------------------
    public function inputActive()
    {
        // Cek login & role
        if (!session()->get('logged_in')) {
            return redirect()->to('/login');
        }
        $role = session()->get('user_role');
        if (!in_array($role, ['superadmin', 'admin'])) {
            return redirect()->to('/dashboard')->with('error', 'Akses ditolak!');
        }

        // Cari jadwal manual yang berstatus published (aktif)
        $activePeriod = $this->periodModel->getActive();
        if (!$activePeriod) {
            return redirect()->to('/manual-schedule')->with('error', 'Tidak ada periode akademik aktif di sistem.');
        }

        $activeSchedule = $this->scheduleModel
            ->where('academic_period_id', $activePeriod['id'])
            ->where('type', 'manual')
            ->where('status', 'published')
            ->first();

        if ($activeSchedule) {
            return redirect()->to('/manual-schedule/edit/' . $activeSchedule['id']);
        }

        // Jika tidak ada yang published, ambil yang paling baru
        $latestSchedule = $this->scheduleModel
            ->where('academic_period_id', $activePeriod['id'])
            ->where('type', 'manual')
            ->orderBy('id', 'DESC')
            ->first();

        if ($latestSchedule) {
            return redirect()->to('/manual-schedule/edit/' . $latestSchedule['id'])
                             ->with('warning', 'Belum ada jadwal manual yang aktif (Terbit). Menampilkan draf jadwal terbaru.');
        }

        return redirect()->to('/manual-schedule')
                         ->with('error', 'Belum ada jadwal manual. Silakan buat template jadwal manual terlebih dahulu.');
    }

    // ------------------------------------------------------------------
    // LIST semua jadwal manual
    // ------------------------------------------------------------------
    public function index()
    {
        // Cek login & role
        if (!session()->get('logged_in')) {
            return redirect()->to('/login');
        }
        $role = session()->get('user_role');
        if (!in_array($role, ['superadmin', 'admin'])) {
            return redirect()->to('/dashboard')->with('error', 'Akses ditolak!');
        }

        // Perbaiki data jika ada schedule manual yang tersimpan sebagai local karena allowedFields sebelumnya belum di-update
        $this->db->table('schedules')
             ->where('type', 'local')
             ->groupStart()
                 ->like('name', 'manual', 'both')
                 ->orLike('name', 'Manual', 'both')
             ->groupEnd()
             ->update(['type' => 'manual']);

        // Ambil jadwal manual saja
        $schedules = $this->scheduleModel->select('schedules.*, academic_years.name as year_name, academic_periods.semester as period_name')
            ->join('academic_years', 'academic_years.id = schedules.academic_year_id', 'left')
            ->join('academic_periods', 'academic_periods.id = schedules.academic_period_id', 'left')
            ->where('schedules.type', 'manual')
            ->orderBy('schedules.id', 'DESC')
            ->findAll();

        foreach ($schedules as &$s) {
            $stats = $this->entryModel->getStats((int)$s['id']);
            $s['total_entries'] = $stats['total'];
        }

        return view('manual-schedule/index', [
            'title'     => 'Pembuat Jadwal Pelajaran Manual',
            'schedules' => $schedules,
        ]);
    }

    // ------------------------------------------------------------------
    // FORM buat jadwal manual baru
    // ------------------------------------------------------------------
    public function create()
    {
        // Cek login & role
        if (!session()->get('logged_in')) {
            return redirect()->to('/login');
        }
        $role = session()->get('user_role');
        if (!in_array($role, ['superadmin', 'admin'])) {
            return redirect()->to('/dashboard')->with('error', 'Akses ditolak!');
        }

        // Ambil periode akademik yang aktif (by system)
        $activePeriod = $this->periodModel->getActive();
        if (!$activePeriod) {
            return redirect()->to('/manual-schedule')->with('error', 'Tidak ada periode akademik aktif di sistem. Silakan atur periode akademik terlebih dahulu.');
        }

        // Ambil total slot KBM di template global per hari
        $timeslotModel = new ScheduleTimeslotModel();
        $slotsRaw = $timeslotModel->select('day, COUNT(id) as total')
            ->where('slot_type', 'kbm')
            ->where('is_active', 1)
            ->groupBy('day')
            ->findAll();

        $defaultSlots = [];
        foreach ($slotsRaw as $row) {
            $defaultSlots[(int)$row['day']] = (int)$row['total'];
        }

        return view('manual-schedule/create', [
            'title'        => 'Buat Template Jadwal Manual',
            'activePeriod' => $activePeriod,
            'defaultSlots' => $defaultSlots,
        ]);
    }

    // ------------------------------------------------------------------
    // SIMPAN template jadwal manual baru
    // ------------------------------------------------------------------
    public function store()
    {
        if (!session()->get('logged_in')) {
            return redirect()->to('/login');
        }
        $role = session()->get('user_role');
        if (!in_array($role, ['superadmin', 'admin'])) {
            return redirect()->to('/dashboard')->with('error', 'Akses ditolak!');
        }

        $rules = [
            'name'               => 'required|max_length[100]',
            'active_days'        => 'required',
        ];

        if (!$this->validate($rules)) {
            return redirect()->back()->withInput()
                             ->with('error', implode(' ', $this->validator->getErrors()));
        }

        // Ambil periode akademik aktif
        $activePeriod = $this->periodModel->getActive();
        if (!$activePeriod) {
            return redirect()->to('/manual-schedule')->with('error', 'Tidak ada periode akademik aktif.');
        }

        // Cek duplikasi jadwal manual untuk periode aktif ini
        $existing = $this->scheduleModel
            ->where('academic_year_id',   $activePeriod['academic_year_id'])
            ->where('academic_period_id', $activePeriod['id'])
            ->where('type',               'manual')
            ->where('name',               $this->request->getPost('name'))
            ->first();

        if ($existing) {
            return redirect()->back()->withInput()
                             ->with('error', 'Jadwal manual dengan nama yang sama untuk periode ini sudah ada.');
        }

        $activeDaysArr = $this->request->getPost('active_days') ?? ['1','2','3','4','5'];
        $activeDays = implode(',', $activeDaysArr);

        // Kustomisasi jam per hari
        $customHours = null;
        if ($this->request->getPost('use_custom_hours') === '1') {
            $hoursMap = [];
            foreach ($activeDaysArr as $day) {
                $hoursMap[(int)$day] = (int)$this->request->getPost('hours_day_' . $day);
            }
            $customHours = json_encode($hoursMap);
        }

        $this->scheduleModel->insert([
            'academic_year_id'   => $activePeriod['academic_year_id'],
            'academic_period_id' => $activePeriod['id'],
            'name'               => $this->request->getPost('name'),
            'active_days'        => $activeDays,
            'type'               => 'manual',
            'custom_hours'       => $customHours,
            'status'             => 'draft',
            'notes'              => $this->request->getPost('notes'),
        ]);

        return redirect()->to('/manual-schedule')
                         ->with('success', 'Template jadwal manual berhasil dibuat. Silakan input jadwal sekarang.');
    }

    // ------------------------------------------------------------------
    // WORKSPACE GRID JADWAL MANUAL
    // ------------------------------------------------------------------
    public function edit(int $id)
    {
        if (!session()->get('logged_in')) {
            return redirect()->to('/login');
        }
        $role = session()->get('user_role');
        if (!in_array($role, ['superadmin', 'admin'])) {
            return redirect()->to('/dashboard')->with('error', 'Akses ditolak!');
        }

        $schedule = $this->scheduleModel->select('schedules.*, academic_years.name as year_name, academic_periods.semester as period_name')
            ->join('academic_years', 'academic_years.id = schedules.academic_year_id', 'left')
            ->join('academic_periods', 'academic_periods.id = schedules.academic_period_id', 'left')
            ->where('schedules.id', $id)
            ->where('schedules.type', 'manual')
            ->first();

        if (!$schedule) {
            return redirect()->to('/manual-schedule')->with('error', 'Jadwal manual tidak ditemukan.');
        }

        // Ambil kelas
        $classrooms = $this->db->table('classrooms c')
            ->select('c.id, c.name, g.name as grade_name, g.id as grade_id')
            ->join('grades g', 'g.id = c.grade_id', 'left')
            ->where('c.deleted_at', null)
            ->orderBy('g.order')
            ->orderBy('c.name')
            ->get()->getResultArray();

        // Mengelompokkan kelas berdasarkan tingkat (Grade)
        $groupedClassrooms = [];
        foreach ($classrooms as $c) {
            $groupedClassrooms[$c['grade_name']][] = $c;
        }

        // Pilihan kelas terpilih
        $selectedClassroomId = (int)($this->request->getGet('classroom_id') ?? ($classrooms[0]['id'] ?? 0));

        // Hari Aktif
        $activeDays = array_map('intval', explode(',', $schedule['active_days']));
        $dayNames = ['', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];

        // Kustomisasi jumlah jam per hari
        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];

        // AUTO-PROVISION: Buat timeslot di database jika belum ada untuk slot yang di-input manual (misal slot 8, 9, 10)
        foreach ($activeDays as $day) {
            $limit = isset($customHours[$day]) ? (int)$customHours[$day] : 7;
            for ($i = 1; $i <= $limit; $i++) {
                $exists = $this->db->table('schedule_timeslots')
                    ->where('day', $day)
                    ->where('slot_number', $i)
                    ->countAllResults();
                if ($exists === 0) {
                    $this->db->table('schedule_timeslots')->insert([
                        'day'         => $day,
                        'slot_number' => $i,
                        'slot_type'   => 'kbm',
                        'start_time'  => '00:00:00',
                        'end_time'    => '00:00:00',
                        'is_active'   => 1,
                        'created_at'  => date('Y-m-d H:i:s'),
                        'updated_at'  => date('Y-m-d H:i:s'),
                    ]);
                }
            }
        }

        // Ambil timeslots aktif per hari
        $timeslotModel = new ScheduleTimeslotModel();
        $timeslotsRaw = $timeslotModel
            ->whereIn('day', $activeDays)
            ->where('is_active', 1)
            ->orderBy('day')
            ->orderBy('slot_number')
            ->findAll();

        // Kustomisasi jumlah jam per hari
        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];

        // Kelompokkan timeslot per hari dengan batasan jumlah jam kustom
        $groupedTimeslots = [];
        $maxSlotNumber = 0;
        foreach ($timeslotsRaw as $slot) {
            $day = (int)$slot['day'];
            $slotNum = (int)$slot['slot_number'];
            
            // Batasi slot berdasarkan custom_hours jika diset
            if (isset($customHours[$day])) {
                if ($slotNum > $customHours[$day]) {
                    continue; // Lewati jika melebihi batas jam kustom
                }
            }

            if ($slotNum > $maxSlotNumber) {
                $maxSlotNumber = $slotNum;
            }
            $groupedTimeslots[$day][$slotNum] = $slot;
        }

        // Ambil guru aktif untuk pilihan
        $teachers = $this->db->table('teachers')
            ->select('id, name, nip, short_code')
            ->where('deleted_at', null)
            ->where('is_active', 1)
            ->orderBy('name')
            ->get()->getResultArray();

        // Ambil mapel aktif untuk pilihan
        $subjects = $this->db->table('school_subjects')
            ->select('id, name, code')
            ->where('is_active', 1)
            ->orderBy('name')
            ->get()->getResultArray();

        // Ambil entri jadwal saat ini untuk template ini
        $gridByClassroom = $this->entryModel->getGridByClassroom($id);

        return view('manual-schedule/edit', [
            'title'               => 'Workspace: ' . $schedule['name'],
            'schedule'            => $schedule,
            'groupedClassrooms'   => $groupedClassrooms,
            'selectedClassroomId' => $selectedClassroomId,
            'activeDays'          => $activeDays,
            'dayNames'            => $dayNames,
            'groupedTimeslots'    => $groupedTimeslots,
            'maxSlotNumber'       => $maxSlotNumber,
            'teachers'            => $teachers,
            'subjects'            => $subjects,
            'gridByClassroom'     => $gridByClassroom,
        ]);
    }

    // ------------------------------------------------------------------
    // SIMPAN ENTRI GRID JADWAL MANUAL (AJAX)
    // ------------------------------------------------------------------
    public function saveEntry()
    {
        if (!$this->request->isAJAX()) {
            return $this->response->setStatusCode(403)->setJSON(['success' => false, 'message' => 'Invalid Request']);
        }

        $json        = $this->request->getJSON(true);
        $scheduleId  = (int)($json['schedule_id'] ?? 0);
        $classroomId = (int)($json['classroom_id'] ?? 0);
        $day         = (int)($json['day'] ?? 0);
        $slotNumber  = (int)($json['slot_number'] ?? 0);
        $teacherId   = (int)($json['teacher_id'] ?? 0);
        $subjectId   = (int)($json['school_subject_id'] ?? 0);
        $duration    = (int)($json['duration'] ?? 1);

        if (!$scheduleId || !$classroomId || !$day || !$slotNumber || !$teacherId || !$subjectId) {
            return $this->response->setJSON(['success' => false, 'message' => 'Lengkapi isian Guru dan Mata Pelajaran terlebih dahulu.']);
        }

        if ($duration < 1) $duration = 1;
        if ($duration > 5) $duration = 5;

        $schedule = $this->scheduleModel->find($scheduleId);
        if (!$schedule || $schedule['type'] !== 'manual') {
            return $this->response->setJSON(['success' => false, 'message' => 'Jadwal manual tidak valid.']);
        }

        // Cek batasan jam kustom hari ini jika diatur
        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];
        if (isset($customHours[$day])) {
            $limit = (int)$customHours[$day];
            if (($slotNumber + $duration - 1) > $limit) {
                return $this->response->setJSON([
                    'success' => false,
                    'message' => "❌ Durasi pelajaran ({$duration} JP) melebihi sisa jam pelajaran hari ini (maksimum jam ke-{$limit})."
                ]);
            }
        }

        // 1. VALIDASI BENTROK GURU UNTUK SEMUA SLOT DALAM BLOK
        for ($i = 0; $i < $duration; $i++) {
            $targetSlot = $slotNumber + $i;

            $teacherConflict = $this->db->table('schedule_entries se')
                ->select('se.id, ss.name as subject_name, c.name as classroom_name')
                ->join('school_subjects ss', 'ss.id = se.school_subject_id', 'left')
                ->join('classrooms c', 'c.id = se.classroom_id', 'left')
                ->where('se.schedule_id', $scheduleId)
                ->where('se.teacher_id', $teacherId)
                ->where('se.day', $day)
                ->where('se.slot_number', $targetSlot)
                ->where('se.classroom_id !=', $classroomId) // Kecuali kelas bersangkutan
                ->get()->getRowArray();

            if ($teacherConflict) {
                $dayName = ScheduleTimeslotModel::dayName($day);
                return $this->response->setJSON([
                    'success' => false,
                    'message' => "❌ Konflik Mengajar di Jam Ke-{$targetSlot}: Guru ini sudah dijadwalkan mengajar <strong>{$teacherConflict['subject_name']}</strong> di kelas <strong>{$teacherConflict['classroom_name']}</strong> pada hari {$dayName} jam ke-{$targetSlot}.",
                ]);
            }
        }

        // 2. AMBIL ATAU BUAT BEBAN MENGAJAR (LOAD) & PENUGASAN (ASSIGNMENT)
        $load = $this->db->table('schedule_loads')
            ->where('academic_year_id',   $schedule['academic_year_id'])
            ->where('academic_period_id', $schedule['academic_period_id'])
            ->where('teacher_id',         $teacherId)
            ->where('school_subject_id',  $subjectId)
            ->where('classroom_id',       $classroomId)
            ->get()->getRowArray();

        if ($load) {
            $loadId = (int)$load['id'];
        } else {
            // Buat beban mengajar baru on-the-fly
            $this->db->table('schedule_loads')->insert([
                'academic_year_id'   => $schedule['academic_year_id'],
                'academic_period_id' => $schedule['academic_period_id'],
                'teacher_id'         => $teacherId,
                'school_subject_id'  => $subjectId,
                'classroom_id'       => $classroomId,
                'hours_per_week'     => 0, // Nilai default 0
                'created_at'         => date('Y-m-d H:i:s'),
                'updated_at'         => date('Y-m-d H:i:s'),
            ]);
            $loadId = $this->db->insertID();

            // Cek apakah teaching_assignments sudah ada untuk (classroom, subject, period) untuk mencegah Duplicate Entry
            $existingAssignment = $this->db->table('teaching_assignments')
                ->where('classroom_id', $classroomId)
                ->where('subject_id', $subjectId)
                ->where('academic_period_id', $schedule['academic_period_id'])
                ->get()->getRowArray();

            if ($existingAssignment) {
                // Update guru pengampu ke guru yang baru dipilih
                $this->db->table('teaching_assignments')
                    ->where('id', $existingAssignment['id'])
                    ->update([
                        'teacher_id' => $teacherId,
                        'updated_at' => date('Y-m-d H:i:s'),
                    ]);
            } else {
                // Masukkan data baru jika belum ada
                $this->db->table('teaching_assignments')->insert([
                    'academic_period_id' => $schedule['academic_period_id'],
                    'teacher_id'         => $teacherId,
                    'subject_id'         => $subjectId,
                    'classroom_id'       => $classroomId,
                    'created_at'         => date('Y-m-d H:i:s'),
                    'updated_at'         => date('Y-m-d H:i:s'),
                ]);
            }
        }

        // 3. SIMPAN TIAP ENTRI SLOT PELAJARAN BERTAHAP
        for ($i = 0; $i < $duration; $i++) {
            $targetSlot = $slotNumber + $i;

            $timeslot = $this->db->table('schedule_timeslots')
                ->where('day', $day)
                ->where('slot_number', $targetSlot)
                ->where('is_active', 1)
                ->get()->getRowArray();

            if (!$timeslot) {
                // Auto-create jika belum ada (safety fallback)
                $this->db->table('schedule_timeslots')->insert([
                    'day'         => $day,
                    'slot_number' => $targetSlot,
                    'slot_type'   => 'kbm',
                    'start_time'  => '00:00:00',
                    'end_time'    => '00:00:00',
                    'is_active'   => 1,
                ]);
                $timeslotId = $this->db->insertID();
            } else {
                $timeslotId = (int)$timeslot['id'];
            }

            $existingEntry = $this->entryModel
                ->where('schedule_id',  $scheduleId)
                ->where('classroom_id', $classroomId)
                ->where('day',          $day)
                ->where('slot_number',  $targetSlot)
                ->first();

            $entryData = [
                'schedule_id'       => $scheduleId,
                'schedule_load_id'  => $loadId,
                'teacher_id'        => $teacherId,
                'classroom_id'      => $classroomId,
                'school_subject_id' => $subjectId,
                'day'               => $day,
                'slot_number'       => $targetSlot,
                'timeslot_id'       => $timeslotId,
                'is_manual'         => 1,
                'is_locked'         => 1,
            ];

            if ($existingEntry) {
                $this->entryModel->update((int)$existingEntry['id'], $entryData);
            } else {
                $this->entryModel->insert($entryData);
            }
        }

        return $this->response->setJSON(['success' => true, 'message' => "Jadwal pelajaran selama {$duration} JP berhasil disimpan!"]);
    }

    // ------------------------------------------------------------------
    // HAPUS ENTRI GRID JADWAL MANUAL (AJAX)
    // ------------------------------------------------------------------
    public function deleteEntry()
    {
        if (!$this->request->isAJAX()) {
            return $this->response->setStatusCode(403)->setJSON(['success' => false, 'message' => 'Invalid Request']);
        }

        $json        = $this->request->getJSON(true);
        $scheduleId  = (int)($json['schedule_id'] ?? 0);
        $classroomId = (int)($json['classroom_id'] ?? 0);
        $day         = (int)($json['day'] ?? 0);
        $slotNumber  = (int)($json['slot_number'] ?? 0);

        if (!$scheduleId || !$classroomId || !$day || !$slotNumber) {
            return $this->response->setJSON(['success' => false, 'message' => 'Data tidak lengkap.']);
        }

        $this->entryModel
             ->where('schedule_id',  $scheduleId)
             ->where('classroom_id', $classroomId)
             ->where('day',          $day)
             ->where('slot_number',  $slotNumber)
             ->delete();

        return $this->response->setJSON(['success' => true, 'message' => 'Jadwal pelajaran berhasil dihapus!']);
    }

    // ------------------------------------------------------------------
    // PUBLIKASIKAN JADWAL MANUAL (AKTIFKAN)
    // ------------------------------------------------------------------
    public function publish(int $id)
    {
        if (!session()->get('logged_in')) {
            return redirect()->to('/login');
        }
        $role = session()->get('user_role');
        if (!in_array($role, ['superadmin', 'admin'])) {
            return redirect()->to('/dashboard')->with('error', 'Akses ditolak!');
        }

        $schedule = $this->scheduleModel->find($id);
        if (!$schedule || $schedule['type'] !== 'manual') {
            return redirect()->to('/manual-schedule')->with('error', 'Jadwal manual tidak ditemukan.');
        }

        // 1. Unpublish semua jadwal manual pada semester/periode ini
        $this->scheduleModel->db->table('schedules')
             ->where('academic_period_id', $schedule['academic_period_id'])
             ->where('type',               'manual')
             ->update(['status' => 'draft']);

        // 2. Set jadwal manual ini menjadi published (aktif)
        $this->scheduleModel->update($id, ['status' => 'published']);

        return redirect()->to('/manual-schedule')->with('success', 'Jadwal manual "' . $schedule['name'] . '" berhasil diaktifkan/dipublikasikan.');
    }

    // ------------------------------------------------------------------
    // HAPUS TEMPLATE JADWAL MANUAL
    // ------------------------------------------------------------------
    public function delete(int $id)
    {
        if (!session()->get('logged_in')) {
            return redirect()->to('/login');
        }
        $role = session()->get('user_role');
        if (!in_array($role, ['superadmin', 'admin'])) {
            return redirect()->to('/dashboard')->with('error', 'Akses ditolak!');
        }

        $schedule = $this->scheduleModel->find($id);
        if (!$schedule || $schedule['type'] !== 'manual') {
            return redirect()->to('/manual-schedule')->with('error', 'Jadwal manual tidak ditemukan.');
        }

        // Hapus entri jadwal
        $this->entryModel->deleteAll($id);
        // Hapus header jadwal
        $this->scheduleModel->delete($id);

        return redirect()->to('/manual-schedule')->with('success', 'Jadwal manual berhasil dihapus.');
    }

    // ══════════════════════════════════════════════════════════════════════
    // VIEW JADWAL MANUAL PER KELAS
    // ══════════════════════════════════════════════════════════════════════
    public function byClass(int $id)
    {
        if (!session()->get('logged_in')) return redirect()->to('/login');
        
        $schedule = $this->scheduleModel->select('schedules.*, academic_years.name as year_name, academic_periods.semester as period_name')
            ->join('academic_years', 'academic_years.id = schedules.academic_year_id', 'left')
            ->join('academic_periods', 'academic_periods.id = schedules.academic_period_id', 'left')
            ->find($id);

        if (!$schedule || $schedule['type'] !== 'manual') {
            return redirect()->to('/manual-schedule')->with('error', 'Jadwal manual tidak ditemukan.');
        }

        $classes = $this->db->table('classrooms c')
            ->select('c.id, c.name, g.name as grade_name, g.id as grade_id')
            ->join('grades g', 'g.id = c.grade_id', 'left')
            ->where('c.deleted_at', null)
            ->orderBy('g.order')
            ->orderBy('c.name')
            ->get()->getResultArray();

        if (empty($classes)) {
            return redirect()->back()->with('error', 'Belum ada data kelas di sistem.');
        }

        $classroomId = (int)($this->request->getGet('classroom_id') ?? $classes[0]['id']);

        $selectedClassroom = null;
        foreach ($classes as $c) {
            if ($c['id'] == $classroomId) {
                $selectedClassroom = $c;
                break;
            }
        }

        // Days & Periods
        $dayNames = ['', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
        $activeDays = array_map('intval', explode(',', $schedule['active_days']));
        $days = [];
        foreach ($activeDays as $dNum) {
            $days[] = [
                'day_number' => $dNum,
                'day_name'   => $dayNames[$dNum] ?? ''
            ];
        }

        $timeslotModel = new ScheduleTimeslotModel();
        $timeslotsRaw = $timeslotModel
            ->whereIn('day', $activeDays)
            ->where('is_active', 1)
            ->orderBy('day')
            ->orderBy('slot_number')
            ->findAll();

        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];
        $maxSlotNumber = 0;
        $filteredRaw = [];
        foreach ($timeslotsRaw as $slot) {
            $day = (int)$slot['day'];
            $slotNum = (int)$slot['slot_number'];
            if (isset($customHours[$day])) {
                if ($slotNum > $customHours[$day]) {
                    continue;
                }
            }
            if ($slotNum > $maxSlotNumber) {
                $maxSlotNumber = $slotNum;
            }
            $filteredRaw[] = $slot;
        }

        $periods = [];
        for ($i = 1; $i <= $maxSlotNumber; $i++) {
            $startTime = '00:00:00';
            $endTime = '00:00:00';
            foreach ($filteredRaw as $slot) {
                if ((int)$slot['slot_number'] === $i) {
                    $startTime = $slot['start_time'];
                    $endTime = $slot['end_time'];
                    break;
                }
            }
            $periods[] = [
                'period_number' => $i,
                'start_time'    => $startTime,
                'end_time'      => $endTime
            ];
        }

        // Grid Data
        $grid = $this->entryModel->getGridByClassroom($id);
        $scheduleData = $grid[$classroomId] ?? [];

        $this->injectAutoColors($scheduleData);

        // All Teachers (for legend)
        $teachers = $this->db->table('teachers t')
            ->select('t.id, t.name, t.short_code, (SELECT MIN(color) FROM asc_teachers WHERE teacher_id = t.id) as color')
            ->where('t.deleted_at', null)
            ->where('t.is_active', 1)
            ->orderBy('t.name')
            ->get()->getResultArray();

        $schedulesList = $this->scheduleModel
            ->where('academic_period_id', $schedule['academic_period_id'])
            ->where('type', 'manual')
            ->findAll();

        return view('manual-schedule/by_class', [
            'title'             => 'Cetak Jadwal: ' . $schedule['name'],
            'schedule'          => $schedule,
            'schedules'         => $schedulesList,
            'classes'           => $classes,
            'classroom_id'      => $classroomId,
            'selected_classroom'=> $selectedClassroom,
            'days'              => $days,
            'periods'           => $periods,
            'schedule_data'     => $scheduleData,
            'teachers'          => $teachers,
        ]);
    }

    // ══════════════════════════════════════════════════════════════════════
    // VIEW JADWAL MANUAL PER GURU
    // ══════════════════════════════════════════════════════════════════════
    public function byTeacher(int $id)
    {
        if (!session()->get('logged_in')) return redirect()->to('/login');

        $schedule = $this->scheduleModel->select('schedules.*, academic_years.name as year_name, academic_periods.semester as period_name')
            ->join('academic_years', 'academic_years.id = schedules.academic_year_id', 'left')
            ->join('academic_periods', 'academic_periods.id = schedules.academic_period_id', 'left')
            ->find($id);

        if (!$schedule || $schedule['type'] !== 'manual') {
            return redirect()->to('/manual-schedule')->with('error', 'Jadwal manual tidak ditemukan.');
        }

        $teachers = $this->db->table('teachers t')
            ->select('t.id, t.name, t.nip, t.short_code, (SELECT MIN(color) FROM asc_teachers WHERE teacher_id = t.id) as color')
            ->where('t.deleted_at', null)
            ->where('t.is_active', 1)
            ->orderBy('t.name')
            ->get()->getResultArray();

        if (empty($teachers)) {
            return redirect()->back()->with('error', 'Belum ada data guru di sistem.');
        }

        $teacherId = (int)($this->request->getGet('teacher_id') ?? $teachers[0]['id']);

        $selectedTeacher = null;
        foreach ($teachers as $t) {
            if ($t['id'] == $teacherId) {
                $selectedTeacher = $t;
                break;
            }
        }

        // Days & Periods
        $dayNames = ['', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
        $activeDays = array_map('intval', explode(',', $schedule['active_days']));
        $days = [];
        foreach ($activeDays as $dNum) {
            $days[] = [
                'day_number' => $dNum,
                'day_name'   => $dayNames[$dNum] ?? ''
            ];
        }

        $timeslotModel = new ScheduleTimeslotModel();
        $timeslotsRaw = $timeslotModel
            ->whereIn('day', $activeDays)
            ->where('is_active', 1)
            ->orderBy('day')
            ->orderBy('slot_number')
            ->findAll();

        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];
        $maxSlotNumber = 0;
        $filteredRaw = [];
        foreach ($timeslotsRaw as $slot) {
            $day = (int)$slot['day'];
            $slotNum = (int)$slot['slot_number'];
            if (isset($customHours[$day])) {
                if ($slotNum > $customHours[$day]) {
                    continue;
                }
            }
            if ($slotNum > $maxSlotNumber) {
                $maxSlotNumber = $slotNum;
            }
            $filteredRaw[] = $slot;
        }

        $periods = [];
        for ($i = 1; $i <= $maxSlotNumber; $i++) {
            $startTime = '00:00:00';
            $endTime = '00:00:00';
            foreach ($filteredRaw as $slot) {
                if ((int)$slot['slot_number'] === $i) {
                    $startTime = $slot['start_time'];
                    $endTime = $slot['end_time'];
                    break;
                }
            }
            $periods[] = [
                'period_number' => $i,
                'start_time'    => $startTime,
                'end_time'      => $endTime
            ];
        }

        // Grid Data
        $grid = $this->entryModel->getGridByTeacher($id);
        $scheduleData = $grid[$teacherId] ?? [];

        $this->injectAutoColors($scheduleData);

        // Resolve warna guru yang dipilih untuk info card
        if ($selectedTeacher) {
            $selectedTeacher['color'] = $this->resolveTeacherColor((int)$selectedTeacher['id'], $selectedTeacher['color'] ?? null);
        }

        // Count Total Hours
        $totalHours = 0;
        foreach ($scheduleData as $daySlots) {
            $totalHours += count($daySlots);
        }

        $schedulesList = $this->scheduleModel
            ->where('academic_period_id', $schedule['academic_period_id'])
            ->where('type', 'manual')
            ->findAll();

        $classes = $this->db->table('classrooms c')
            ->select('c.id, c.name, g.name as grade_name')
            ->join('grades g', 'g.id = c.grade_id', 'left')
            ->where('c.deleted_at', null)
            ->orderBy('g.order')
            ->orderBy('c.name')
            ->get()->getResultArray();

        return view('manual-schedule/by_teacher', [
            'title'           => 'Cetak Jadwal: ' . $schedule['name'],
            'schedule'        => $schedule,
            'schedules'       => $schedulesList,
            'teachers'        => $teachers,
            'teacher_id'      => $teacherId,
            'selected_teacher'=> $selectedTeacher,
            'days'            => $days,
            'periods'         => $periods,
            'schedule_data'   => $scheduleData,
            'total_hours'     => $totalHours,
            'classes'         => $classes,
        ]);
    }

    // ══════════════════════════════════════════════════════════════════════
    // EXPORTS CLASS
    // ══════════════════════════════════════════════════════════════════════

    public function exportClassExcel(int $id)
    {
        $this->generateClassExcelHandler($id, true);
    }

    public function exportClassExcelNoTimes(int $id)
    {
        $this->generateClassExcelHandler($id, false);
    }

    private function generateClassExcelHandler(int $id, bool $showTime)
    {
        if (!session()->get('logged_in')) exit;

        $schedule = $this->scheduleModel->select('schedules.*, academic_years.name as year_name, academic_periods.semester as period_name')
            ->join('academic_years', 'academic_years.id = schedules.academic_year_id', 'left')
            ->join('academic_periods', 'academic_periods.id = schedules.academic_period_id', 'left')
            ->find($id);

        if (!$schedule || $schedule['type'] !== 'manual') exit;

        $classroomId = (int)$this->request->getGet('classroom_id');
        $classroom = $this->db->table('classrooms')->where('id', $classroomId)->get()->getRowArray();
        if (!$classroom) exit;

        // Days & Periods
        $dayNames = ['', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
        $activeDays = array_map('intval', explode(',', $schedule['active_days']));
        $days = [];
        foreach ($activeDays as $dNum) {
            $days[] = [
                'day_number' => $dNum,
                'day_name'   => $dayNames[$dNum] ?? ''
            ];
        }

        $timeslotModel = new ScheduleTimeslotModel();
        $timeslotsRaw = $timeslotModel
            ->whereIn('day', $activeDays)
            ->where('is_active', 1)
            ->orderBy('day')
            ->orderBy('slot_number')
            ->findAll();

        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];
        $maxSlotNumber = 0;
        $filteredRaw = [];
        foreach ($timeslotsRaw as $slot) {
            $day = (int)$slot['day'];
            $slotNum = (int)$slot['slot_number'];
            if (isset($customHours[$day])) {
                if ($slotNum > $customHours[$day]) {
                    continue;
                }
            }
            if ($slotNum > $maxSlotNumber) {
                $maxSlotNumber = $slotNum;
            }
            $filteredRaw[] = $slot;
        }

        $periods = [];
        for ($i = 1; $i <= $maxSlotNumber; $i++) {
            $startTime = '00:00:00';
            $endTime = '00:00:00';
            foreach ($filteredRaw as $slot) {
                if ((int)$slot['slot_number'] === $i) {
                    $startTime = $slot['start_time'];
                    $endTime = $slot['end_time'];
                    break;
                }
            }
            $periods[] = [
                'period_number' => $i,
                'start_time'    => $startTime,
                'end_time'      => $endTime
            ];
        }

        $grid = $this->entryModel->getGridByClassroom($id);
        $scheduleData = $grid[$classroomId] ?? [];
        $this->injectAutoColors($scheduleData);

        $teachers = $this->db->table('teachers t')
            ->select('t.id, t.name, t.short_code, IFNULL((SELECT MIN(at.color) FROM asc_teachers at WHERE at.teacher_id = t.id), "#17a2b8") as color')
            ->where('t.deleted_at', null)
            ->where('t.is_active', 1)
            ->get()->getResultArray();

        $this->generateClassExcel($schedule, $classroom, $periods, $days, $scheduleData, $teachers, $showTime);
    }

    private function generateClassExcel($schedule, $classroom, $periods, $days, $scheduleData, $teachers, $showTime = true)
    {
        $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
        $sheet       = $spreadsheet->getActiveSheet();
        $sheet->setTitle('Jadwal Kelas');

        $Coord = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::class;
        $colPeriods = count($periods);
        $lastDataCol = $colPeriods + 1;
        $lastColStr  = $Coord::stringFromColumnIndex($lastDataCol);

        // Header Title
        $sheet->mergeCells("A1:{$lastColStr}1");
        $sheet->setCellValue('A1', 'JADWAL PELAJARAN KELAS ' . strtoupper($classroom['name']));
        $sheet->getStyle('A1')->applyFromArray([
            'font'      => ['bold' => true, 'size' => 14],
            'alignment' => ['horizontal' => 'center'],
        ]);
        $sheet->getRowDimension(1)->setRowHeight(24);

        $sheet->mergeCells("A2:{$lastColStr}2");
        $sheet->setCellValue('A2', $schedule['name'] . ' (T.A. ' . $schedule['year_name'] . ' — ' . $schedule['period_name'] . ')');
        $sheet->getStyle('A2')->applyFromArray([
            'font'      => ['size' => 10],
            'alignment' => ['horizontal' => 'center'],
        ]);

        // Table Header
        $headerStyle = [
            'font'      => ['bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 9],
            'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => '1e3a5f']],
            'alignment' => ['horizontal' => 'center', 'vertical' => 'center', 'wrapText' => true],
            'borders'   => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => '555555']]],
        ];

        $sheet->setCellValue('A4', 'Hari');
        $sheet->getStyle('A4')->applyFromArray($headerStyle);
        $sheet->getColumnDimension('A')->setWidth(9);

        foreach ($periods as $idx => $period) {
            $col = $Coord::stringFromColumnIndex($idx + 2);
            $sheet->setCellValue($col . '4', $showTime ? ($period['period_number'] . "\n" . substr($period['start_time'], 0, 5) . '-' . substr($period['end_time'], 0, 5)) : ('Jam ' . $period['period_number']));
            $sheet->getStyle($col . '4')->applyFromArray($headerStyle);
            $sheet->getColumnDimension($col)->setWidth(13);
        }
        $sheet->getRowDimension(4)->setRowHeight(28);

        // Data Rows
        $dataRow = 5;
        foreach ($days as $day) {
            $dayNum = $day['day_number'];
            $sheet->setCellValue('A' . $dataRow, strtoupper(substr($day['day_name'], 0, 3)));
            $sheet->getStyle('A' . $dataRow)->applyFromArray([
                'font'      => ['bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 9],
                'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => '1e3a5f']],
                'alignment' => ['horizontal' => 'center', 'vertical' => 'center'],
                'borders'   => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => '555555']]],
            ]);

            foreach ($periods as $idx => $period) {
                $pNum = $period['period_number'];
                $col  = $Coord::stringFromColumnIndex($idx + 2);
                $addr = $col . $dataRow;
                $slot = isset($scheduleData[$dayNum][$pNum]) ? $scheduleData[$dayNum][$pNum][0] : null;

                if ($slot) {
                    $rawColor = $slot['teacher_color'] ?? '#17a2b8';
                    $bgColor = ltrim($rawColor, '#');
                    $fgColor = $this->getExcelTextColor($bgColor);
                    
                    $displayCode = $slot['subject_code'] ?: $slot['subject_name'];
                    $tName = $slot['teacher_short_code'] ?: $slot['teacher_name'];

                    $sheet->setCellValue($addr, $displayCode . "\n[" . $tName . "]");
                    $sheet->getStyle($addr)->applyFromArray([
                        'font'      => ['bold' => true, 'size' => 9, 'color' => ['rgb' => $fgColor]],
                        'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => $bgColor]],
                        'alignment' => ['horizontal' => 'center', 'vertical' => 'center', 'wrapText' => true],
                        'borders'   => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => 'AAAAAA']]],
                    ]);
                } else {
                    $sheet->getStyle($addr)->applyFromArray([
                        'fill'    => ['fillType' => 'solid', 'startColor' => ['rgb' => 'F5F5F5']],
                        'borders' => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => 'DDDDDD']]],
                    ]);
                }
            }
            $sheet->getRowDimension($dataRow)->setRowHeight(30);
            $dataRow++;

            // Separation bar
            $sheet->getRowDimension($dataRow)->setRowHeight(4);
            $sheet->mergeCells("A{$dataRow}:{$lastColStr}{$dataRow}");
            $sheet->getStyle("A{$dataRow}:{$lastColStr}{$dataRow}")->applyFromArray([
                'fill' => ['fillType' => 'solid', 'startColor' => ['rgb' => '1e3a5f']],
            ]);
            $dataRow++;
        }

        // Legend Kode Guru
        $dataRow++;
        $sheet->mergeCells("A{$dataRow}:{$lastColStr}{$dataRow}");
        $sheet->setCellValue('A' . $dataRow, 'KETERANGAN KODE GURU');
        $sheet->getStyle('A' . $dataRow)->applyFromArray([
            'font'      => ['bold' => true, 'size' => 10, 'color' => ['rgb' => '1e3a5f']],
            'alignment' => ['horizontal' => 'center'],
        ]);
        $sheet->getRowDimension($dataRow)->setRowHeight(18);
        $dataRow++;

        $sheet->setCellValue('A' . $dataRow, 'Kode');
        $sheet->setCellValue('B' . $dataRow, 'Nama Guru');
        $sheet->getStyle('A' . $dataRow . ':B' . $dataRow)->applyFromArray([
            'font'      => ['bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 9],
            'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => '2d4f7c']],
            'alignment' => ['horizontal' => 'center'],
            'borders'   => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => '555555']]],
        ]);
        $dataRow++;

        $usedTeachers = [];
        foreach ($scheduleData as $daySlots) {
            foreach ($daySlots as $slotRows) {
                foreach ($slotRows as $slot) {
                    $code = $slot['teacher_short_code'] ?: $slot['teacher_name'];
                    $usedTeachers[$code] = $slot['teacher_name'];
                }
            }
        }
        ksort($usedTeachers);

        foreach ($usedTeachers as $code => $name) {
            $sheet->setCellValue('A' . $dataRow, $code);
            $sheet->setCellValue('B' . $dataRow, $name);
            $sheet->getStyle('A' . $dataRow . ':B' . $dataRow)->applyFromArray([
                'font'    => ['size' => 9],
                'borders' => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => 'CCCCCC']]],
            ]);
            $sheet->getStyle('A' . $dataRow)->getAlignment()->setHorizontal('center');
            $dataRow++;
        }

        $filename = 'jadwal_manual_kelas_' . preg_replace('/[^a-zA-Z0-9_-]/', '_', $classroom['name']) . '_' . date('Ymd') . '.xlsx';
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment;filename="' . $filename . '"');
        header('Cache-Control: max-age=0');
        $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
        $writer->save('php://output');
        exit;
    }

    public function exportClassPdf(int $id)
    {
        $this->generateClassPdfHandler($id, true, false);
    }

    public function exportClassPdfNoTimes(int $id)
    {
        $this->generateClassPdfHandler($id, false, false);
    }

    public function exportAllClassPdf(int $id)
    {
        $this->generateClassPdfHandler($id, true, true);
    }

    public function exportAllClassPdfNoTimes(int $id)
    {
        $this->generateClassPdfHandler($id, false, true);
    }

    private function generateClassPdfHandler(int $id, bool $showTime, bool $allClasses)
    {
        if (!session()->get('logged_in')) exit;

        $schedule = $this->scheduleModel->select('schedules.*, academic_years.name as year_name, academic_periods.semester as period_name')
            ->join('academic_years', 'academic_years.id = schedules.academic_year_id', 'left')
            ->join('academic_periods', 'academic_periods.id = schedules.academic_period_id', 'left')
            ->find($id);

        if (!$schedule || $schedule['type'] !== 'manual') exit;

        // Days & Periods
        $dayNames = ['', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
        $activeDays = array_map('intval', explode(',', $schedule['active_days']));
        $days = [];
        foreach ($activeDays as $dNum) {
            $days[] = [
                'day_number' => $dNum,
                'day_name'   => $dayNames[$dNum] ?? ''
            ];
        }

        $timeslotModel = new ScheduleTimeslotModel();
        $timeslotsRaw = $timeslotModel
            ->whereIn('day', $activeDays)
            ->where('is_active', 1)
            ->orderBy('day')
            ->orderBy('slot_number')
            ->findAll();

        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];
        $maxSlotNumber = 0;
        $filteredRaw = [];
        foreach ($timeslotsRaw as $slot) {
            $day = (int)$slot['day'];
            $slotNum = (int)$slot['slot_number'];
            if (isset($customHours[$day])) {
                if ($slotNum > $customHours[$day]) {
                    continue;
                }
            }
            if ($slotNum > $maxSlotNumber) {
                $maxSlotNumber = $slotNum;
            }
            $filteredRaw[] = $slot;
        }

        $periods = [];
        for ($i = 1; $i <= $maxSlotNumber; $i++) {
            $startTime = '00:00:00';
            $endTime = '00:00:00';
            foreach ($filteredRaw as $slot) {
                if ((int)$slot['slot_number'] === $i) {
                    $startTime = $slot['start_time'];
                    $endTime = $slot['end_time'];
                    break;
                }
            }
            $periods[] = [
                'period_number' => $i,
                'start_time'    => $startTime,
                'end_time'      => $endTime
            ];
        }

        $classes = $this->db->table('classrooms c')
            ->select('c.*, g.name as grade_name')
            ->join('grades g', 'g.id = c.grade_id', 'left')
            ->where('c.deleted_at', null)
            ->orderBy('g.order')
            ->orderBy('c.name')
            ->get()->getResultArray();

        $grid = $this->entryModel->getGridByClassroom($id);

        $mpdf = new \Mpdf\Mpdf([
            'orientation' => 'L',
            'margin_top'  => 10,
            'margin_bottom' => 10,
            'margin_left' => 8,
            'margin_right' => 8,
            'default_font_size' => 9,
            'default_font' => 'dejavusans',
        ]);

        if ($allClasses) {
            foreach ($classes as $idx => $c) {
                $scheduleData = $grid[$c['id']] ?? [];
                $this->injectAutoColors($scheduleData);
                if ($idx > 0) $mpdf->AddPage();
                $html = $this->buildClassPdfHtml($schedule, $c, $periods, $days, $scheduleData, $showTime);
                $mpdf->WriteHTML($html);
            }
            $filename = 'jadwal_manual_semua_kelas_' . date('Ymd') . '.pdf';
        } else {
            $classroomId = (int)$this->request->getGet('classroom_id');
            $classroom = null;
            foreach ($classes as $c) {
                if ($c['id'] == $classroomId) {
                    $classroom = $c;
                    break;
                }
            }
            if (!$classroom) exit;
            $scheduleData = $grid[$classroomId] ?? [];
            $this->injectAutoColors($scheduleData);
            $html = $this->buildClassPdfHtml($schedule, $classroom, $periods, $days, $scheduleData, $showTime);
            $mpdf->WriteHTML($html);
            $filename = 'jadwal_manual_kelas_' . preg_replace('/[^a-zA-Z0-9_-]/', '_', $classroom['name']) . '_' . date('Ymd') . '.pdf';
        }

        $mpdf->Output($filename, 'D');
        exit;
    }

    private function buildClassPdfHtml($schedule, $classroom, $periods, $days, $scheduleData, $showTime)
    {
        $html = '<style>
            .header { text-align: center; margin-bottom: 12px; }
            .header h2 { margin: 0; font-size: 14pt; color: #1e3a5f; text-transform: uppercase; }
            .header p { margin: 3px 0 0 0; font-size: 9pt; color: #555; }
            .schedule-table { width: 100%; border-collapse: collapse; margin-top: 5px; table-layout: fixed; }
            .schedule-table th, .schedule-table td { border: 1px solid #444; text-align: center; vertical-align: middle; padding: 4px; }
            .schedule-table th { background-color: #1e3a5f; color: #ffffff; font-size: 8.5pt; font-weight: bold; height: 26px; }
            .td-jam { background-color: #f0f4f8; font-weight: bold; font-size: 8pt; width: 11%; }
            .td-empty { background-color: #fafafa; color: #aaa; }
            .legend-container { margin-top: 15px; border: 1px solid #aaa; padding: 8px; background-color: #fff; }
            .legend-title { font-size: 8.5pt; font-weight: bold; color: #1e3a5f; border-bottom: 1px solid #aaa; padding-bottom: 3px; margin-bottom: 5px; text-align: center; }
            .legend-list { font-size: 7.5pt; }
            .legend-col { width: 49%; float: left; }
            .legend-col-right { width: 49%; float: right; }
            .legend-item { margin-bottom: 2px; }
            .legend-item .code { font-weight: bold; color: #1e3a5f; display: inline-block; width: 40px; }
            .legend-item .separator { margin: 0 4px; color: #aaa; }
            .legend-item .name { color: #333; }
            .clearfix { clear: both; }
        </style>';

        $html .= '<div class="header">';
        $html .= '<h2>JADWAL PELAJARAN KELAS ' . htmlspecialchars($classroom['name']) . '</h2>';
        $html .= '<p>' . htmlspecialchars($schedule['name']) . ' (T.A. ' . htmlspecialchars($schedule['year_name']) . ' — ' . htmlspecialchars($schedule['period_name']) . ')</p>';
        $html .= '</div>';

        if (!empty($periods) && !empty($days)) {
            $colWidth = round(89 / count($days), 1);
            
            $html .= '<table class="schedule-table">';
            $html .= '<thead>';
            $html .= '<tr>';
            $html .= '<th style="width:11%">Jam</th>';
            foreach ($days as $day) {
                $html .= '<th style="width:' . $colWidth . '%">' . strtoupper($day['day_name']) . '</th>';
            }
            $html .= '</tr>';
            $html .= '</thead>';
            $html .= '<tbody>';

            foreach ($periods as $period) {
                $pNum = $period['period_number'];
                $html .= '<tr>';
                $html .= '<td class="td-jam">';
                $html .= 'Jam ' . $pNum;
                if ($showTime) {
                    $html .= '<br><span style="font-size:7pt;font-weight:normal">' . substr($period['start_time'], 0, 5) . '-' . substr($period['end_time'], 0, 5) . '</span>';
                }
                $html .= '</td>';

                foreach ($days as $day) {
                    $slot = isset($scheduleData[$day['day_number']][$pNum]) ? $scheduleData[$day['day_number']][$pNum][0] : null;

                    if ($slot) {
                        $rawColor = $slot['teacher_color'] ?? '#17a2b8';
                        $bg = $rawColor;
                        $fg = $this->getExcelTextColor(ltrim($bg, '#'));
                        $displayCode = $slot['subject_code'] ?: $slot['subject_name'];
                        $tCode = $slot['teacher_short_code'] ?: $slot['teacher_name'];
                        $html .= '<td style="background:' . $bg . ';color:#' . $fg . ';font-weight:bold">'
                            . htmlspecialchars($displayCode)
                            . '<br><span style="font-size:7pt;opacity:0.8;font-weight:normal">[' . htmlspecialchars($tCode) . ']</span>'
                            . '</td>';
                    } else {
                        $html .= '<td class="td-empty">-</td>';
                    }
                }
                $html .= '</tr>';
            }

            $html .= '</tbody>';
            $html .= '</table>';
        }

        // Legend Kode Guru
        $usedTeachers = [];
        if (!empty($scheduleData)) {
            foreach ($scheduleData as $daySlots) {
                foreach ($daySlots as $slotRows) {
                    foreach ($slotRows as $slot) {
                        $code = $slot['teacher_short_code'] ?: $slot['teacher_name'];
                        if (!isset($usedTeachers[$code])) {
                            $usedTeachers[$code] = [
                                'code' => $code,
                                'name' => $slot['teacher_name']
                            ];
                        }
                    }
                }
            }
        }

        if (!empty($usedTeachers)) {
            ksort($usedTeachers);
            $teacherList = array_values($usedTeachers);
            $totalTeachers = count($teacherList);
            $halfCount = ceil($totalTeachers / 2);
            
            $html .= '<div class="legend-container">';
            $html .= '<div class="legend-title">KETERANGAN KODE GURU</div>';
            $html .= '<div class="legend-list">';
            
            $html .= '<div class="legend-col">';
            for ($i = 0; $i < $halfCount; $i++) {
                if (isset($teacherList[$i])) {
                    $html .= '<div class="legend-item">';
                    $html .= '<span class="code">' . htmlspecialchars($teacherList[$i]['code']) . '</span>';
                    $html .= '<span class="separator">-</span>';
                    $html .= '<span class="name">' . htmlspecialchars($teacherList[$i]['name']) . '</span>';
                    $html .= '</div>';
                }
            }
            $html .= '</div>';
            
            $html .= '<div class="legend-col-right">';
            for ($i = $halfCount; $i < $totalTeachers; $i++) {
                if (isset($teacherList[$i])) {
                    $html .= '<div class="legend-item">';
                    $html .= '<span class="code">' . htmlspecialchars($teacherList[$i]['code']) . '</span>';
                    $html .= '<span class="separator">-</span>';
                    $html .= '<span class="name">' . htmlspecialchars($teacherList[$i]['name']) . '</span>';
                    $html .= '</div>';
                }
            }
            $html .= '</div>';
            
            $html .= '<div class="clearfix"></div>';
            $html .= '</div>';
            $html .= '</div>';
        }

        return $html;
    }

    // ══════════════════════════════════════════════════════════════════════
    // EXPORTS TEACHER
    // ══════════════════════════════════════════════════════════════════════

    public function exportTeacherExcel(int $id)
    {
        $this->generateTeacherExcelHandler($id, true);
    }

    public function exportTeacherExcelNoTimes(int $id)
    {
        $this->generateTeacherExcelHandler($id, false);
    }

    private function generateTeacherExcelHandler(int $id, bool $showTime)
    {
        if (!session()->get('logged_in')) exit;

        $schedule = $this->scheduleModel->select('schedules.*, academic_years.name as year_name, academic_periods.semester as period_name')
            ->join('academic_years', 'academic_years.id = schedules.academic_year_id', 'left')
            ->join('academic_periods', 'academic_periods.id = schedules.academic_period_id', 'left')
            ->find($id);

        if (!$schedule || $schedule['type'] !== 'manual') exit;

        $teacherId = (int)$this->request->getGet('teacher_id');
        $teacher = $this->db->table('teachers t')
            ->select('t.*, IFNULL((SELECT MIN(at.color) FROM asc_teachers at WHERE at.teacher_id = t.id), "#17a2b8") as color')
            ->where('t.id', $teacherId)
            ->get()->getRowArray();
        if (!$teacher) exit;

        // Days & Periods
        $dayNames = ['', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
        $activeDays = array_map('intval', explode(',', $schedule['active_days']));
        $days = [];
        foreach ($activeDays as $dNum) {
            $days[] = [
                'day_number' => $dNum,
                'day_name'   => $dayNames[$dNum] ?? ''
            ];
        }

        $timeslotModel = new ScheduleTimeslotModel();
        $timeslotsRaw = $timeslotModel
            ->whereIn('day', $activeDays)
            ->where('is_active', 1)
            ->orderBy('day')
            ->orderBy('slot_number')
            ->findAll();

        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];
        $maxSlotNumber = 0;
        $filteredRaw = [];
        foreach ($timeslotsRaw as $slot) {
            $day = (int)$slot['day'];
            $slotNum = (int)$slot['slot_number'];
            if (isset($customHours[$day])) {
                if ($slotNum > $customHours[$day]) {
                    continue;
                }
            }
            if ($slotNum > $maxSlotNumber) {
                $maxSlotNumber = $slotNum;
            }
            $filteredRaw[] = $slot;
        }

        $periods = [];
        for ($i = 1; $i <= $maxSlotNumber; $i++) {
            $startTime = '00:00:00';
            $endTime = '00:00:00';
            foreach ($filteredRaw as $slot) {
                if ((int)$slot['slot_number'] === $i) {
                    $startTime = $slot['start_time'];
                    $endTime = $slot['end_time'];
                    break;
                }
            }
            $periods[] = [
                'period_number' => $i,
                'start_time'    => $startTime,
                'end_time'      => $endTime
            ];
        }

        $grid = $this->entryModel->getGridByTeacher($id);
        $scheduleData = $grid[$teacherId] ?? [];
        // Resolve teacher color jika tidak ada di asc_teachers
        $teacher['color'] = $this->resolveTeacherColor((int)$teacher['id'], $teacher['color'] ?? null);

        $this->generateTeacherExcel($schedule, $teacher, $periods, $days, $scheduleData, $showTime);
    }

    private function generateTeacherExcel($schedule, $teacher, $periods, $days, $scheduleData, $showTime = true)
    {
        $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
        $sheet       = $spreadsheet->getActiveSheet();
        $sheet->setTitle('Jadwal Guru');

        $Coord   = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::class;
        $rawColor = $teacher['color'] ?? '#17a2b8';
        $bgColor = ltrim($rawColor, '#');
        $fgColor = $this->getExcelTextColor($bgColor);

        $colPeriods = count($periods);
        $lastDataCol = $colPeriods + 1;
        $lastColStr  = $Coord::stringFromColumnIndex($lastDataCol);

        // Header Title
        $sheet->mergeCells("A1:{$lastColStr}1");
        $sheet->setCellValue('A1', 'JADWAL MENGAJAR GURU');
        $sheet->getStyle('A1')->applyFromArray([
            'font'      => ['bold' => true, 'size' => 14],
            'alignment' => ['horizontal' => 'center'],
        ]);
        $sheet->getRowDimension(1)->setRowHeight(24);

        $sheet->mergeCells("A2:{$lastColStr}2");
        $sheet->setCellValue('A2', $teacher['name'] . ' (' . ($teacher['short_code'] ?: '-') . ')');
        $sheet->getStyle('A2')->applyFromArray([
            'font'      => ['bold' => true, 'size' => 12, 'color' => ['rgb' => $fgColor]],
            'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => $bgColor]],
            'alignment' => ['horizontal' => 'center', 'vertical' => 'center'],
        ]);
        $sheet->getRowDimension(2)->setRowHeight(22);

        $sheet->mergeCells("A3:{$lastColStr}3");
        $sheet->setCellValue('A3', $schedule['name'] . ' (T.A. ' . $schedule['year_name'] . ' — ' . $schedule['period_name'] . ')');
        $sheet->getStyle('A3')->applyFromArray([
            'font'      => ['size' => 10],
            'alignment' => ['horizontal' => 'center'],
        ]);

        // Table Header
        $headerStyle = [
            'font'      => ['bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 9],
            'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => '1e3a5f']],
            'alignment' => ['horizontal' => 'center', 'vertical' => 'center', 'wrapText' => true],
            'borders'   => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => '555555']]],
        ];

        $sheet->setCellValue('A5', 'Hari');
        $sheet->getStyle('A5')->applyFromArray($headerStyle);
        $sheet->getColumnDimension('A')->setWidth(9);

        foreach ($periods as $idx => $period) {
            $col = $Coord::stringFromColumnIndex($idx + 2);
            $sheet->setCellValue($col . '5', $showTime ? ($period['period_number'] . "\n" . substr($period['start_time'], 0, 5) . '-' . substr($period['end_time'], 0, 5)) : ('Jam ' . $period['period_number']));
            $sheet->getStyle($col . '5')->applyFromArray($headerStyle);
            $sheet->getColumnDimension($col)->setWidth(13);
        }
        $sheet->getRowDimension(5)->setRowHeight(28);

        // Data Rows
        $dataRow = 6;
        $totalHours = 0;
        $classSummary = [];

        foreach ($days as $day) {
            $dayNum = $day['day_number'];
            $sheet->setCellValue('A' . $dataRow, strtoupper(substr($day['day_name'], 0, 3)));
            $sheet->getStyle('A' . $dataRow)->applyFromArray([
                'font'      => ['bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 9],
                'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => '1e3a5f']],
                'alignment' => ['horizontal' => 'center', 'vertical' => 'center'],
                'borders'   => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => '555555']]],
            ]);

            foreach ($periods as $idx => $period) {
                $pNum = $period['period_number'];
                $col  = $Coord::stringFromColumnIndex($idx + 2);
                $addr = $col . $dataRow;
                $slot = isset($scheduleData[$dayNum][$pNum]) ? $scheduleData[$dayNum][$pNum][0] : null;

                if ($slot) {
                    $sheet->setCellValue($addr, $slot['classroom_name'] . "\n" . ($slot['subject_code'] ?: $slot['subject_name']));
                    $sheet->getStyle($addr)->applyFromArray([
                        'font'      => ['bold' => true, 'size' => 9, 'color' => ['rgb' => $fgColor]],
                        'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => $bgColor]],
                        'alignment' => ['horizontal' => 'center', 'vertical' => 'center', 'wrapText' => true],
                        'borders'   => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => 'AAAAAA']]],
                    ]);
                    $totalHours++;
                    $key = $slot['classroom_name'];
                    if (!isset($classSummary[$key])) $classSummary[$key] = ['hours' => 0, 'subjects' => []];
                    $classSummary[$key]['hours']++;
                    $classSummary[$key]['subjects'][$slot['subject_name']] = true;
                } else {
                    $sheet->getStyle($addr)->applyFromArray([
                        'fill'    => ['fillType' => 'solid', 'startColor' => ['rgb' => 'F5F5F5']],
                        'borders' => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => 'DDDDDD']]],
                    ]);
                }
            }
            $sheet->getRowDimension($dataRow)->setRowHeight(30);
            $dataRow++;

            // Separation bar
            $sheet->getRowDimension($dataRow)->setRowHeight(4);
            $sheet->mergeCells("A{$dataRow}:{$lastColStr}{$dataRow}");
            $sheet->getStyle("A{$dataRow}:{$lastColStr}{$dataRow}")->applyFromArray([
                'fill' => ['fillType' => 'solid', 'startColor' => ['rgb' => '1e3a5f']],
            ]);
            $dataRow++;
        }

        // Teaching Summary
        $dataRow++;
        $sheet->mergeCells("A{$dataRow}:{$lastColStr}{$dataRow}");
        $sheet->setCellValue('A' . $dataRow, 'RINGKASAN MENGAJAR — Total: ' . $totalHours . ' jam/minggu');
        $sheet->getStyle('A' . $dataRow)->applyFromArray([
            'font'      => ['bold' => true, 'color' => ['rgb' => $fgColor], 'size' => 10],
            'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => $bgColor]],
            'alignment' => ['horizontal' => 'center'],
        ]);
        $sheet->getRowDimension($dataRow)->setRowHeight(18);
        $dataRow++;

        $sheet->setCellValue('A' . $dataRow, 'Kelas');
        $sheet->setCellValue('B' . $dataRow, 'Mata Pelajaran');
        $sheet->setCellValue('C' . $dataRow, 'Jam');
        $sheet->getStyle('A' . $dataRow . ':C' . $dataRow)->applyFromArray([
            'font'      => ['bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 9],
            'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => '2d4f7c']],
            'alignment' => ['horizontal' => 'center'],
            'borders'   => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => '555555']]],
        ]);
        $dataRow++;

        ksort($classSummary);
        foreach ($classSummary as $className => $summary) {
            $sheet->setCellValue('A' . $dataRow, $className);
            $sheet->setCellValue('B' . $dataRow, implode(', ', array_keys($summary['subjects'])));
            $sheet->setCellValue('C' . $dataRow, $summary['hours']);
            $sheet->getStyle('A' . $dataRow . ':C' . $dataRow)->applyFromArray([
                'font'    => ['size' => 9],
                'borders' => ['allBorders' => ['borderStyle' => 'thin', 'color' => ['rgb' => 'CCCCCC']]],
                'alignment' => ['vertical' => 'center'],
            ]);
            $sheet->getStyle('A' . $dataRow)->applyFromArray([
                'font'      => ['bold' => true, 'color' => ['rgb' => $fgColor], 'size' => 9],
                'fill'      => ['fillType' => 'solid', 'startColor' => ['rgb' => $bgColor]],
                'alignment' => ['horizontal' => 'center'],
            ]);
            $sheet->getStyle('C' . $dataRow)->getAlignment()->setHorizontal('center');
            $dataRow++;
        }

        $filename = 'jadwal_manual_guru_' . preg_replace('/[^a-zA-Z0-9_-]/', '_', $teacher['name']) . '_' . date('Ymd') . '.xlsx';
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment;filename="' . $filename . '"');
        header('Cache-Control: max-age=0');
        $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
        $writer->save('php://output');
        exit;
    }

    public function exportTeacherPdf(int $id)
    {
        $this->generateTeacherPdfHandler($id, true, false);
    }

    public function exportTeacherPdfNoTimes(int $id)
    {
        $this->generateTeacherPdfHandler($id, false, false);
    }

    public function exportAllTeacherPdf(int $id)
    {
        $this->generateTeacherPdfHandler($id, true, true);
    }

    public function exportAllTeacherPdfNoTimes(int $id)
    {
        $this->generateTeacherPdfHandler($id, false, true);
    }

    private function generateTeacherPdfHandler(int $id, bool $showTime, bool $allTeachers)
    {
        if (!session()->get('logged_in')) exit;

        $schedule = $this->scheduleModel->select('schedules.*, academic_years.name as year_name, academic_periods.semester as period_name')
            ->join('academic_years', 'academic_years.id = schedules.academic_year_id', 'left')
            ->join('academic_periods', 'academic_periods.id = schedules.academic_period_id', 'left')
            ->find($id);

        if (!$schedule || $schedule['type'] !== 'manual') exit;

        // Days & Periods
        $dayNames = ['', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
        $activeDays = array_map('intval', explode(',', $schedule['active_days']));
        $days = [];
        foreach ($activeDays as $dNum) {
            $days[] = [
                'day_number' => $dNum,
                'day_name'   => $dayNames[$dNum] ?? ''
            ];
        }

        $timeslotModel = new ScheduleTimeslotModel();
        $timeslotsRaw = $timeslotModel
            ->whereIn('day', $activeDays)
            ->where('is_active', 1)
            ->orderBy('day')
            ->orderBy('slot_number')
            ->findAll();

        $customHours = $schedule['custom_hours'] ? json_decode($schedule['custom_hours'], true) : [];
        $maxSlotNumber = 0;
        $filteredRaw = [];
        foreach ($timeslotsRaw as $slot) {
            $day = (int)$slot['day'];
            $slotNum = (int)$slot['slot_number'];
            if (isset($customHours[$day])) {
                if ($slotNum > $customHours[$day]) {
                    continue;
                }
            }
            if ($slotNum > $maxSlotNumber) {
                $maxSlotNumber = $slotNum;
            }
            $filteredRaw[] = $slot;
        }

        $periods = [];
        for ($i = 1; $i <= $maxSlotNumber; $i++) {
            $startTime = '00:00:00';
            $endTime = '00:00:00';
            foreach ($filteredRaw as $slot) {
                if ((int)$slot['slot_number'] === $i) {
                    $startTime = $slot['start_time'];
                    $endTime = $slot['end_time'];
                    break;
                }
            }
            $periods[] = [
                'period_number' => $i,
                'start_time'    => $startTime,
                'end_time'      => $endTime
            ];
        }

        $teachers = $this->db->table('teachers t')
            ->select('t.id, t.name, t.nip, t.short_code, IFNULL((SELECT MIN(at.color) FROM asc_teachers at WHERE at.teacher_id = t.id), "#17a2b8") as color')
            ->where('t.deleted_at', null)
            ->where('t.is_active', 1)
            ->orderBy('t.name')
            ->get()->getResultArray();

        $grid = $this->entryModel->getGridByTeacher($id);

        $mpdf = new \Mpdf\Mpdf([
            'orientation' => 'L',
            'margin_top'  => 10,
            'margin_bottom' => 10,
            'margin_left' => 8,
            'margin_right' => 8,
            'default_font_size' => 9,
            'default_font' => 'dejavusans',
        ]);

        if ($allTeachers) {
            $first = true;
            foreach ($teachers as $t) {
                $scheduleData = $grid[$t['id']] ?? [];
                if (empty($scheduleData)) continue;
                $t['color'] = $this->resolveTeacherColor((int)$t['id'], $t['color'] ?? null);
                if (!$first) $mpdf->AddPage();
                $first = false;
                $html = $this->buildTeacherPdfHtml($schedule, $t, $periods, $days, $scheduleData, $showTime);
                $mpdf->WriteHTML($html);
            }
            $filename = 'jadwal_manual_semua_guru_' . date('Ymd') . '.pdf';
        } else {
            $teacherId = (int)$this->request->getGet('teacher_id');
            $teacher = null;
            foreach ($teachers as $t) {
                if ($t['id'] == $teacherId) {
                    $teacher = $t;
                    break;
                }
            }
            if (!$teacher) exit;
            $teacher['color'] = $this->resolveTeacherColor((int)$teacher['id'], $teacher['color'] ?? null);
            $scheduleData = $grid[$teacherId] ?? [];
            $html = $this->buildTeacherPdfHtml($schedule, $teacher, $periods, $days, $scheduleData, $showTime);
            $mpdf->WriteHTML($html);
            $filename = 'jadwal_manual_guru_' . preg_replace('/[^a-zA-Z0-9_-]/', '_', $teacher['name']) . '_' . date('Ymd') . '.pdf';
        }

        $mpdf->Output($filename, 'D');
        exit;
    }

    private function buildTeacherPdfHtml($schedule, $teacher, $periods, $days, $scheduleData, $showTime)
    {
        $teacherBg = $teacher['color'] ?? '#17a2b8';
        $teacherFg = $this->getExcelTextColor(ltrim($teacherBg, '#'));

        $html = '<style>
            .header { text-align: center; margin-bottom: 12px; }
            .header h2 { margin: 0; font-size: 14pt; color: #1e3a5f; text-transform: uppercase; }
            .header p { margin: 3px 0 0 0; font-size: 9pt; color: #555; }
            .teacher-bar { background-color: ' . $teacherBg . '; color: #' . $teacherFg . '; font-weight: bold; font-size: 11pt; padding: 5px; text-align: center; margin-bottom: 10px; border: 1px solid #333; }
            .schedule-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
            .schedule-table th, .schedule-table td { border: 1px solid #444; text-align: center; vertical-align: middle; padding: 4px; }
            .schedule-table th { background-color: #1e3a5f; color: #ffffff; font-size: 8.5pt; font-weight: bold; height: 26px; }
            .td-jam { background-color: ' . $teacherBg . '20; color: #000; font-weight: bold; font-size: 8pt; width: 11%; }
            .td-empty { background-color: #fafafa; color: #aaa; }
            .summary-container { margin-top: 15px; }
            .summary-title { font-size: 9pt; font-weight: bold; color: #' . $teacherFg . '; background-color: ' . $teacherBg . '; padding: 4px 8px; border: 1px solid #333; }
            .summary-table { width: 100%; border-collapse: collapse; margin-top: 5px; }
            .summary-table th, .summary-table td { border: 1px solid #ccc; padding: 4px 8px; font-size: 8pt; }
            .summary-table th { background-color: #2d4f7c; color: #fff; text-align: center; font-weight: bold; }
        </style>';

        $html .= '<div class="header">';
        $html .= '<h2>JADWAL MENGAJAR GURU</h2>';
        $html .= '<p>' . htmlspecialchars($schedule['name']) . ' (T.A. ' . htmlspecialchars($schedule['year_name']) . ' — ' . htmlspecialchars($schedule['period_name']) . ')</p>';
        $html .= '</div>';

        $html .= '<div class="teacher-bar">' . htmlspecialchars($teacher['name']) . ' (' . ($teacher['short_code'] ?: '-') . ')</div>';

        $totalHours = 0;
        $classSummary = [];

        if (!empty($periods) && !empty($days)) {
            $colWidth = round(89 / count($days), 1);
            
            $html .= '<table class="schedule-table">';
            $html .= '<thead>';
            $html .= '<tr>';
            $html .= '<th style="width:11%">Jam</th>';
            foreach ($days as $day) {
                $html .= '<th style="width:' . $colWidth . '%">' . strtoupper($day['day_name']) . '</th>';
            }
            $html .= '</tr>';
            $html .= '</thead>';
            $html .= '<tbody>';

            foreach ($periods as $period) {
                $pNum = $period['period_number'];
                $html .= '<tr>';
                $html .= '<td class="td-jam">';
                $html .= 'Jam ' . $pNum;
                if ($showTime) {
                    $html .= '<br><span style="font-size:7pt;font-weight:normal">' . substr($period['start_time'], 0, 5) . '-' . substr($period['end_time'], 0, 5) . '</span>';
                }
                $html .= '</td>';

                foreach ($days as $day) {
                    $slot = isset($scheduleData[$day['day_number']][$pNum]) ? $scheduleData[$day['day_number']][$pNum][0] : null;

                    if ($slot) {
                        $html .= '<td style="background:' . $teacherBg . ';color:#' . $teacherFg . ';font-weight:bold">'
                            . htmlspecialchars($slot['classroom_name'])
                            . '<br><span style="font-size:7pt;opacity:0.8;font-weight:normal">' . htmlspecialchars($slot['subject_code'] ?: $slot['subject_name']) . '</span>'
                            . '</td>';
                        $totalHours++;
                        $key = $slot['classroom_name'];
                        if (!isset($classSummary[$key])) $classSummary[$key] = ['hours' => 0, 'subjects' => []];
                        $classSummary[$key]['hours']++;
                        $classSummary[$key]['subjects'][$slot['subject_name']] = true;
                    } else {
                        $html .= '<td class="td-empty">-</td>';
                    }
                }
                $html .= '</tr>';
            }

            $html .= '</tbody>';
            $html .= '</table>';
        }

        // Summary Table
        if (!empty($classSummary)) {
            ksort($classSummary);
            $html .= '<div class="summary-container">';
            $html .= '<div class="summary-title">RINGKASAN MENGAJAR — Total: ' . $totalHours . ' jam/minggu</div>';
            $html .= '<table class="summary-table">';
            $html .= '<thead><tr><th style="width:25%">Kelas</th><th style="width:60%">Mata Pelajaran</th><th style="width:15%">Jam</th></tr></thead>';
            $html .= '<tbody>';
            foreach ($classSummary as $className => $summary) {
                $html .= '<tr>';
                $html .= '<td style="font-weight:bold;background:' . $teacherBg . '15">' . htmlspecialchars($className) . '</td>';
                $html .= '<td>' . htmlspecialchars(implode(', ', array_keys($summary['subjects']))) . '</td>';
                $html .= '<td style="text-align:center">' . $summary['hours'] . '</td>';
                $html .= '</tr>';
            }
            $html .= '</tbody></table></div>';
        }

        return $html;
    }

    private function getContrastColor($hex)
    {
        $hex = ltrim($hex, '#');
        if (strlen($hex) !== 6) return '#000000';
        $r = hexdec(substr($hex, 0, 2));
        $g = hexdec(substr($hex, 2, 2));
        $b = hexdec(substr($hex, 4, 2));
        $luminance = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255;
        return $luminance > 0.5 ? '#000000' : '#ffffff';
    }

    private function getExcelTextColor($hex)
    {
        $color = $this->getContrastColor($hex);
        return ltrim($color, '#');
    }

    /**
     * Inject warna HSL merata ke semua slot dalam $scheduleData yang teacher_color-nya kosong.
     * Warna di-assign berdasarkan posisi teacher_id (sorted) → hue merata 0..360.
     */
    private function injectAutoColors(array &$scheduleData): void
    {
        if (empty($scheduleData)) return;

        $teacherColorMap = [];
        $noColorIds = [];
        foreach ($scheduleData as $daySlots) {
            foreach ($daySlots as $slotRows) {
                foreach ($slotRows as $slot) {
                    $tid = (int)$slot['teacher_id'];
                    if (!isset($teacherColorMap[$tid])) {
                        $teacherColorMap[$tid] = $slot['teacher_color'] ?? null;
                        if (empty($slot['teacher_color'])) {
                            $noColorIds[$tid] = true;
                        }
                    }
                }
            }
        }
        if (!empty($noColorIds)) {
            $ids   = array_keys($noColorIds);
            sort($ids);
            $total = count($ids);
            foreach ($ids as $i => $tid) {
                $hue = (int)(($i * 360) / $total);
                $teacherColorMap[$tid] = $this->hslToHex($hue, 65, 40);
            }
        }
        foreach ($scheduleData as &$daySlots) {
            foreach ($daySlots as &$slotRows) {
                foreach ($slotRows as &$slot) {
                    if (empty($slot['teacher_color'])) {
                        $slot['teacher_color'] = $teacherColorMap[(int)$slot['teacher_id']] ?? '#17a2b8';
                    }
                }
            }
        }
        unset($daySlots, $slotRows, $slot);
    }

    /**
     * Resolve warna satu guru: pakai $existingColor jika ada, fallback ke golden-angle dari teacher_id.
     */
    private function resolveTeacherColor(int $teacherId, ?string $existingColor): string
    {
        if (!empty($existingColor) && $existingColor !== '#17a2b8') {
            return $existingColor;
        }
        // Golden-angle: distribusi merata berdasarkan ID
        $hue = (int)(($teacherId * 137) % 360);
        return $this->hslToHex($hue, 65, 40);
    }

    /**
     * Konversi HSL ke hex (#rrggbb) — untuk auto-warna guru jadwal manual
     */
    private function hslToHex(int $h, int $s, int $l): string
    {
        $s /= 100;
        $l /= 100;
        $c  = (1 - abs(2 * $l - 1)) * $s;
        $x  = $c * (1 - abs(fmod($h / 60, 2) - 1));
        $m  = $l - $c / 2;
        if ($h < 60)      { $r = $c; $g = $x; $b = 0; }
        elseif ($h < 120) { $r = $x; $g = $c; $b = 0; }
        elseif ($h < 180) { $r = 0;  $g = $c; $b = $x; }
        elseif ($h < 240) { $r = 0;  $g = $x; $b = $c; }
        elseif ($h < 300) { $r = $x; $g = 0;  $b = $c; }
        else              { $r = $c; $g = 0;  $b = $x; }
        return sprintf('#%02x%02x%02x',
            (int)(($r + $m) * 255),
            (int)(($g + $m) * 255),
            (int)(($b + $m) * 255)
        );
    }
}
