<?php

namespace App\Controllers\View;

use App\Controllers\BaseController;
use App\Models\ConfigurationModel;
use App\Models\CalendarModel;
use App\Models\HomeworkModel;

class HomeworkTrackingController extends BaseController
{
    protected $configModel;
    protected $calendarModel;
    protected $homeworkModel;
    protected $db;

    protected string $semester;
    protected string $schoolYear;
    /** cache for teacher assignment presence per term */
    private array $teacherAssignmentCache = [];

    public function __construct()
    {
        $this->configModel   = new ConfigurationModel();
        $this->calendarModel = new CalendarModel();
        $this->homeworkModel = new HomeworkModel();
        $this->db            = \Config\Database::connect();

        $this->semester   = (string) ($this->configModel->getConfig('semester') ?? '');
        $this->schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
    }

    public function index()
    {
        // Semester-based date range (Fall or Spring) derived from school year start.
        [$start, $end] = $this->getDateRangeForSemester($this->schoolYear, $this->semester);

        // Move start to the first Sunday on/after start
        if ((int)$start->format('w') !== 0) {
            $start = (clone $start)->modify('next sunday');
        }

        // Collect all Sundays within range
        $sundays = [];
        $d = clone $start;
        while ($d <= $end) {
            $sundays[] = $d->format('Y-m-d');
            $d->modify('+7 days');
        }

        // Map "no school" events by date (Y-m-d) using explicit DB flag
        $events    = $this->calendarModel->getEventsBySchoolYear($this->schoolYear) ?? [];
        $eventDays = [];
        foreach ($events as $ev) {
            $ymd = substr((string)($ev['date'] ?? ''), 0, 10);
            $no  = (int)($ev['no_school'] ?? 0);
            if ($ymd && $no === 1) {
                $eventDays[$ymd] = true; // yellow highlight for no-school day
            }
        }

        // Map Sundays to ordinal homework_index, skipping event Sundays
        $dateToIndex = [];
        $idx = 0;
        foreach ($sundays as $ymd) {
            if (!isset($eventDays[$ymd])) {
                $idx++;
                $dateToIndex[$ymd] = $idx;
            } else {
                $dateToIndex[$ymd] = null; // event day (no homework expected)
            }
        }

        $limitToSemester = $this->hasTeacherAssignments($this->schoolYear, $this->semester);

        // Aggregate homework presence + first entered date per class_section_id + homework_index
        $hwQ = $this->db->table('homework')
            ->select('class_section_id, homework_index, MIN(created_at) AS first_created, COUNT(*) AS cnt')
            ->where('school_year', $this->schoolYear);
        if ($limitToSemester && $this->semester !== '') {
            $hwQ->where('semester', $this->semester);
        }
        $rows = $hwQ->groupBy('class_section_id, homework_index')
            ->get()
            ->getResultArray();

        $hasHomework = [];
        $hwEnteredAt = [];
        $homeworkSubmissionCounts = [];
        foreach ($rows as $r) {
            $csid = (int)($r['class_section_id'] ?? 0);
            $hi   = (int)($r['homework_index'] ?? 0);
            $cnt  = (int)($r['cnt'] ?? 0);
            if ($csid > 0 && $hi > 0 && $cnt > 0) {
                $hasHomework[$csid][$hi] = true;
                $dateStr = substr((string)($r['first_created'] ?? ''), 0, 10);
                $hwEnteredAt[$csid][$hi] = $dateStr ?: null;
                $homeworkSubmissionCounts[$csid] = ($homeworkSubmissionCounts[$csid] ?? 0) + 1;
            }
        }

        // Build date-based presence mapped to the nearest prior non-NoSchool Sunday.
        $hb = $this->db->table('homework')
            ->select("class_section_id, DATE(created_at) AS hw_date, MIN(created_at) AS first_created, COUNT(*) AS cnt", false)
            ->where('school_year', $this->schoolYear);
        if ($limitToSemester && $this->semester !== '') {
            $hb->where('semester', $this->semester);
        }
        $rowsByDate = $hb->groupBy('class_section_id, DATE(created_at)', '', false)
            ->orderBy('hw_date', 'ASC', false)
            ->get()
            ->getResultArray();

        $hasHomeworkByDate = [];
        $hwEnteredAtByDate = [];
        foreach ($rowsByDate as $r) {
            $csid = (int)($r['class_section_id'] ?? 0);
            $d    = substr((string)($r['hw_date'] ?? ''), 0, 10);
            $cnt  = (int)($r['cnt'] ?? 0);
            $firstCreated = substr((string)($r['first_created'] ?? ''), 0, 10);
            if ($csid <= 0 || !$d || $cnt <= 0) { continue; }

            // Find the index of the last Sunday on or before $d
            $baseIndex = -1;
            for ($i = count($sundays) - 1; $i >= 0; $i--) {
                if ($sundays[$i] <= $d) { $baseIndex = $i; break; }
            }
            if ($baseIndex < 0) { continue; }

            // Map this homework day to only the nearest prior non–No School Sunday
            $j = $baseIndex;
            while ($j >= 0 && !empty($eventDays[$sundays[$j]])) { $j--; }
            if ($j < 0) { continue; }
            $sd = $sundays[$j];

            // Mark homework on that Sunday for this class_section (single mapping)
            $hasHomeworkByDate[$csid][$sd] = true;
            if (empty($hwEnteredAtByDate[$csid][$sd])) {
                $hwEnteredAtByDate[$csid][$sd] = $firstCreated ?: $d;
            } else {
                // Keep the earliest date for display
                $existing = (string)$hwEnteredAtByDate[$csid][$sd];
                $candidate = $firstCreated ?: $d;
                if ($candidate && (!$existing || $candidate < $existing)) {
                    $hwEnteredAtByDate[$csid][$sd] = $candidate;
                }
            }
        }

        // Fetch teachers and TAs per section for this term
        $tb = $this->db->table('teacher_class tc')
            ->select('tc.class_section_id, tc.position, cs.class_section_name, cs.class_id, u.firstname, u.lastname')
            ->join('classSection cs', 'cs.class_section_id = tc.class_section_id', 'left')
            ->join('users u', 'u.id = tc.teacher_id', 'left')
            ->where('tc.school_year', $this->schoolYear);
        $teacherRows = $tb->where('tc.class_section_id IS NOT NULL', null, false)
            ->whereIn('tc.position', ['main','ta'])
            ->orderBy('cs.class_id', 'ASC')
            ->orderBy('cs.class_section_name', 'ASC')
            ->orderBy("FIELD(tc.position, 'main','ta')", '', false)
            ->orderBy('u.firstname', 'ASC')
            ->get()
            ->getResultArray();

        $bySection = [];
        foreach ($teacherRows as $r) {
            $sid = (int)($r['class_section_id'] ?? 0);
            if ($sid <= 0) continue;
            if (!isset($bySection[$sid])) {
                $bySection[$sid] = [
                    'class_section_id'   => $sid,
                    'class_id'           => (int)($r['class_id'] ?? 0),
                    'class_section_name' => (string)($r['class_section_name'] ?? ''),
                    'teachers'           => [],
                    'tas'                => [],
                ];
            }
            $name = trim(($r['firstname'] ?? '') . ' ' . ($r['lastname'] ?? ''));
            $pos  = strtolower((string)($r['position'] ?? ''));
            if ($name !== '') {
                if ($pos === 'main') { $bySection[$sid]['teachers'][] = $name; }
                elseif ($pos === 'ta') { $bySection[$sid]['tas'][] = $name; }
            }
        }

        $teachers = array_values($bySection);

        // Sort by grade order: KG(13) → Grade 1..11 → Youth(12) → others
        usort($teachers, function ($a, $b) {
            $ai = (int)($a['class_id'] ?? 0);
            $bi = (int)($b['class_id'] ?? 0);
            $ord = function ($cid) {
                if ($cid === 13) return 0;      // KG first
                if ($cid >= 1 && $cid <= 11) return $cid; // Grades 1..11
                if ($cid === 12) return 99;     // Youth last of known
                return 200 + $cid;              // any others after
            };
            $cmp = $ord($ai) <=> $ord($bi);
            if ($cmp !== 0) return $cmp;
            return strnatcasecmp($a['class_section_name'] ?? '', $b['class_section_name'] ?? '');
        });

        // Simple server-side pagination for rows (8 lines per page)
        $perPage = 8;
        $totalRows = count($teachers);
        $totalPages = max(1, (int)ceil($totalRows / $perPage));
        $page = (int) ($this->request->getGet('page') ?? 1);
        if ($page < 1) $page = 1;
        if ($page > $totalPages) $page = $totalPages;
        $offset = ($page - 1) * $perPage;
        $teachersPage = array_slice($teachers, $offset, $perPage);

        return view('grading/homework_tracking', [
            'semester'     => $this->semester,
            'schoolYear'   => $this->schoolYear,
            'sundays'      => $sundays,
            'eventDays'    => $eventDays,
            'dateToIndex'  => $dateToIndex,
            'teachers'     => $teachersPage,
            'hasHomework'  => $hasHomework,
            'hwEnteredAt'  => $hwEnteredAt,
            'homeworkSubmissionCounts' => $homeworkSubmissionCounts,
            'hasHomeworkByDate' => $hasHomeworkByDate,
            'hwEnteredAtByDate' => $hwEnteredAtByDate,
            'page'         => $page,
            'totalPages'   => $totalPages,
            'perPage'      => $perPage,
            'totalRows'    => $totalRows,
        ]);
    }

    /**
     * Returns true if teacher_class has any rows for the given school year.
     * The semester argument is ignored because assignments are tracked per year.
     */
    private function hasTeacherAssignments(string $schoolYear, string $semester): bool
    {
        $year = trim((string)$schoolYear);
        if ($year === '') {
            return false;
        }

        if (array_key_exists($year, $this->teacherAssignmentCache)) {
            return $this->teacherAssignmentCache[$year];
        }

        $cnt = $this->db->table('teacher_class')
            ->where('school_year', $year)
            ->countAllResults();

        $this->teacherAssignmentCache[$year] = $cnt > 0;
        return $this->teacherAssignmentCache[$year];
    }

    /**
     * Compute start/end dates for the given semester within the school year.
     * Fall: 09/21/{startYear} to 01/18/{startYear+1}
     * Spring: 01/25/{startYear+1} to 05/31/{startYear+1}
     * Defaults to today if parsing fails.
     */
    private function getDateRangeForSemester(string $schoolYear, string $semester): array
    {
        $startYear = null;
        if (preg_match('/\b(20\d{2})\b/', $schoolYear, $m)) {
            $startYear = (int)$m[1];
        }
        if ($startYear === null) {
            $today = new \DateTime('today');
            return [$today, $today];
        }

        $nextYear = $startYear + 1;
        $sem = strtolower(trim($semester));

        $start = $end = null;
        try {
            if ($sem === 'spring') {
                $start = new \DateTime("{$nextYear}-01-25");
                $end   = new \DateTime("{$nextYear}-05-31");
            } else {
                // default/fall
                $start = new \DateTime("{$startYear}-09-21");
                $end   = new \DateTime("{$nextYear}-01-18");
            }
        } catch (\Throwable $e) {
            $today = new \DateTime('today');
            return [$today, $today];
        }

        return [$start, $end];
    }
}
