<?php

namespace App\Modules\Dashboard\Services;

use App\Modules\Auth\Entities\Role;

/**
 * Dashboard Schedule Service
 *
 * Returns today's schedules filtered by user role (Asia/Jakarta).
 */
class DashboardScheduleService
{
    /**
     * Get today's day_of_week (1=Monday, 7=Sunday) in Asia/Jakarta.
     */
    public function getTodayDayOfWeek(): int
    {
        $tz = new \DateTimeZone('Asia/Jakarta');
        $now = new \DateTimeImmutable('now', $tz);
        return (int) $now->format('N');
    }

    /**
     * Get schedules for today filtered by user role.
     * Uses lesson_slots for start/end time and users for teacher_name; fallback to schedule columns when NULL.
     *
     * @param array|null $userContext { id, roles: [ { role_code } ] }
     * @return list<array{schedule_id: int, subject_name: string, class_name: string, teacher_name: string, start_time: string, end_time: string}>
     */
    public function getSchedulesToday(?array $userContext = null): array
    {
        $dayOfWeek = $this->getTodayDayOfWeek();
        $db        = \Config\Database::connect();

        $builder = $db->table('schedules AS sch');
        $builder->select('
            sch.id AS schedule_id,
            COALESCE(sub.name, "-") AS subject_name,
            COALESCE(c.name, "-") AS class_name,
            COALESCE(u.name, sch.teacher_name) AS teacher_name,
            COALESCE(ls.start_time, sch.start_time) AS start_time,
            COALESCE(ls.end_time, sch.end_time) AS end_time
        ');
        $builder->join('lesson_slots AS ls', 'ls.id = sch.lesson_slot_id', 'left');
        $builder->join('users AS u', 'u.id = sch.teacher_user_id', 'left');
        $builder->join('subjects AS sub', 'sub.id = sch.subject_id', 'left');
        $builder->join('classes AS c', 'c.id = sch.class_id', 'left');
        $builder->where('sch.day_of_week', $dayOfWeek);
        $builder->orderBy('COALESCE(ls.start_time, sch.start_time)', 'ASC', false);

        $this->applyRoleFilter($builder, $userContext);

        $rows = $builder->get()->getResultArray();

        $out = [];
        foreach ($rows as $row) {
            $out[] = [
                'schedule_id'   => (int) $row['schedule_id'],
                'subject_name'  => (string) ($row['subject_name'] ?? '-'),
                'class_name'    => (string) ($row['class_name'] ?? '-'),
                'teacher_name'  => (string) ($row['teacher_name'] ?? '-'),
                'start_time'    => (string) $row['start_time'],
                'end_time'      => (string) $row['end_time'],
            ];
        }

        return $out;
    }

    /**
     * Get schedules for a given date (day_of_week from date in Asia/Jakarta). Same shape as getSchedulesToday.
     *
     * @param string $dateYmd Y-m-d
     * @param array|null $userContext
     * @return list<array{schedule_id: int, subject_name: string, class_name: string, teacher_name: string, start_time: string, end_time: string}>
     */
    public function getSchedulesByDate(string $dateYmd, ?array $userContext = null): array
    {
        $tz       = new \DateTimeZone('Asia/Jakarta');
        $date     = new \DateTimeImmutable($dateYmd . ' 12:00:00', $tz);
        $dayOfWeek = (int) $date->format('N');

        $db = \Config\Database::connect();

        $builder = $db->table('schedules AS sch');
        $builder->select('
            sch.id AS schedule_id,
            COALESCE(sub.name, "-") AS subject_name,
            COALESCE(c.name, "-") AS class_name,
            COALESCE(u.name, sch.teacher_name) AS teacher_name,
            COALESCE(ls.start_time, sch.start_time) AS start_time,
            COALESCE(ls.end_time, sch.end_time) AS end_time
        ');
        $builder->join('lesson_slots AS ls', 'ls.id = sch.lesson_slot_id', 'left');
        $builder->join('users AS u', 'u.id = sch.teacher_user_id', 'left');
        $builder->join('subjects AS sub', 'sub.id = sch.subject_id', 'left');
        $builder->join('classes AS c', 'c.id = sch.class_id', 'left');
        $builder->where('sch.day_of_week', $dayOfWeek);
        $builder->orderBy('COALESCE(ls.start_time, sch.start_time)', 'ASC', false);

        $this->applyRoleFilter($builder, $userContext);

        $rows = $builder->get()->getResultArray();

        $out = [];
        foreach ($rows as $row) {
            $out[] = [
                'schedule_id'   => (int) $row['schedule_id'],
                'subject_name'  => (string) ($row['subject_name'] ?? '-'),
                'class_name'    => (string) ($row['class_name'] ?? '-'),
                'teacher_name'  => (string) ($row['teacher_name'] ?? '-'),
                'start_time'    => (string) $row['start_time'],
                'end_time'      => (string) $row['end_time'],
            ];
        }

        return $out;
    }

    /**
     * Get current lesson (schedule active right now) or next upcoming today.
     * Uses Asia/Jakarta. Same RBAC as getSchedulesToday. lesson_slots/users as source of truth with fallback.
     *
     * @param array|null $userContext
     * @return array{is_active_now: bool, schedule_id?: int, subject_name?: string, class_name?: string, teacher_name?: string, start_time?: string, end_time?: string, next_schedule?: array}
     */
    public function getCurrentSchedule(?array $userContext = null): array
    {
        $tz          = new \DateTimeZone('Asia/Jakarta');
        $now         = new \DateTimeImmutable('now', $tz);
        $dayOfWeek   = (int) $now->format('N');
        $currentTime = $now->format('H:i:s');

        $db   = \Config\Database::connect();
        $base = 'sch.id AS schedule_id, sch.class_id AS class_id, COALESCE(sub.name, "-") AS subject_name, COALESCE(c.name, "-") AS class_name, '
            . 'COALESCE(u.name, sch.teacher_name) AS teacher_name, '
            . 'COALESCE(ls.start_time, sch.start_time) AS start_time, COALESCE(ls.end_time, sch.end_time) AS end_time';
        $timeRangeWhere = '( COALESCE(ls.start_time, sch.start_time) <= ' . $db->escape($currentTime)
            . ' AND COALESCE(ls.end_time, sch.end_time) > ' . $db->escape($currentTime) . ' )';

        $builder = $db->table('schedules AS sch');
        $builder->select($base);
        $builder->join('lesson_slots AS ls', 'ls.id = sch.lesson_slot_id', 'left');
        $builder->join('users AS u', 'u.id = sch.teacher_user_id', 'left');
        $builder->join('subjects AS sub', 'sub.id = sch.subject_id', 'left');
        $builder->join('classes AS c', 'c.id = sch.class_id', 'left');
        $builder->where('sch.day_of_week', $dayOfWeek);
        $builder->where($timeRangeWhere);
        $builder->orderBy('COALESCE(ls.start_time, sch.start_time)', 'ASC', false);
        $builder->limit(1);

        $this->applyRoleFilter($builder, $userContext);

        $rows = $builder->get()->getResultArray();

        if (!empty($rows)) {
            $row = $rows[0];
            return [
                'is_active_now' => true,
                'schedule_id'   => (int) $row['schedule_id'],
                'class_id'      => (int) ($row['class_id'] ?? 0),
                'subject_name'  => (string) ($row['subject_name'] ?? '-'),
                'class_name'    => (string) ($row['class_name'] ?? '-'),
                'teacher_name'  => (string) ($row['teacher_name'] ?? '-'),
                'start_time'    => (string) $row['start_time'],
                'end_time'      => (string) $row['end_time'],
            ];
        }

        $nextTimeWhere = '( COALESCE(ls.start_time, sch.start_time) > ' . $db->escape($currentTime) . ' )';
        $nextBuilder = $db->table('schedules AS sch');
        $nextBuilder->select($base);
        $nextBuilder->join('lesson_slots AS ls', 'ls.id = sch.lesson_slot_id', 'left');
        $nextBuilder->join('users AS u', 'u.id = sch.teacher_user_id', 'left');
        $nextBuilder->join('subjects AS sub', 'sub.id = sch.subject_id', 'left');
        $nextBuilder->join('classes AS c', 'c.id = sch.class_id', 'left');
        $nextBuilder->where('sch.day_of_week', $dayOfWeek);
        $nextBuilder->where($nextTimeWhere);
        $nextBuilder->orderBy('COALESCE(ls.start_time, sch.start_time)', 'ASC', false);
        $nextBuilder->limit(1);

        $this->applyRoleFilter($nextBuilder, $userContext);

        $nextRows    = $nextBuilder->get()->getResultArray();
        $nextSchedule = null;
        if (!empty($nextRows)) {
            $r = $nextRows[0];
            $nextSchedule = [
                'schedule_id'  => (int) $r['schedule_id'],
                'subject_name' => (string) ($r['subject_name'] ?? '-'),
                'class_name'   => (string) ($r['class_name'] ?? '-'),
                'teacher_name' => (string) ($r['teacher_name'] ?? '-'),
                'start_time'   => (string) $r['start_time'],
                'end_time'     => (string) $r['end_time'],
            ];
        }

        $result = ['is_active_now' => false];
        if ($nextSchedule !== null) {
            $result['next_schedule'] = $nextSchedule;
        }
        return $result;
    }

    protected function applyRoleFilter($builder, ?array $userContext): void
    {
        if ($userContext === null) {
            return;
        }

        $roles   = $userContext['roles'] ?? [];
        $codes   = array_column($roles, 'role_code');
        $userId  = (int) ($userContext['id'] ?? 0);

        if (in_array(Role::CODE_ADMIN, $codes, true) || in_array(Role::CODE_GURU_BK, $codes, true)) {
            return;
        }

        if (in_array(Role::CODE_WALI_KELAS, $codes, true)) {
            $classIds = $this->getClassIdsForWali($userId);
            if ($classIds === []) {
                $builder->where('1 =', 0);
                return;
            }
            $builder->whereIn('sch.class_id', $classIds);
            return;
        }

        if (in_array(Role::CODE_GURU_MAPEL, $codes, true)) {
            $builder->where('sch.teacher_user_id', $userId);
            return;
        }

        if (in_array(Role::CODE_ORANG_TUA, $codes, true)) {
            $studentIds = $this->getStudentIdsForParent($userId);
            if ($studentIds === []) {
                $builder->where('1 =', 0);
                return;
            }
            $db       = \Config\Database::connect();
            $subQuery = $db->table('students')->select('class_id')->whereIn('id', $studentIds)->get()->getResultArray();
            $classIds = array_values(array_unique(array_map('intval', array_column($subQuery, 'class_id'))));
            if ($classIds === []) {
                $builder->where('1 =', 0);
                return;
            }
            $builder->whereIn('sch.class_id', $classIds);
            return;
        }
    }

    protected function getClassIdsForWali(int $userId): array
    {
        $db   = \Config\Database::connect();
        $rows = $db->table('classes')->select('id')->where('wali_user_id', $userId)->get()->getResultArray();
        return array_map('intval', array_column($rows, 'id'));
    }

    protected function getStudentIdsForParent(int $userId): array
    {
        $db   = \Config\Database::connect();
        $rows = $db->table('student_parents AS sp')
            ->select('sp.student_id')
            ->join('parents AS p', 'p.id = sp.parent_id', 'inner')
            ->where('p.user_id', $userId)
            ->get()
            ->getResultArray();
        return array_map('intval', array_column($rows, 'student_id'));
    }
}
