<?php

namespace App\Controllers\View;

use App\Controllers\BaseController;
use App\Models\TeacherClassModel;
use App\Models\StudentClassModel;
use App\Models\StudentModel;
use App\Models\QuizModel;
use App\Models\HomeworkModel;
use App\Models\FinalExamModel;
use App\Models\MidtermExamModel;
use App\Models\ProjectModel;
use App\Models\SemesterScoreModel;
use App\Models\ConfigurationModel;
use App\Models\ScoreCommentModel;
use App\Models\ParticipationModel;
use App\Models\AttendanceRecordModel;
use App\Models\AttendanceDataModel;
use App\Models\ClassSectionModel;
use App\Models\UserModel;
use CodeIgniter\Events\Events;
use Config\Services;
use App\Models\CalendarModel;
use App\Services\Calculators\AttendanceCalculator;
use App\Models\GradingLockModel;
use App\Models\MissingScoreOverrideModel;



class ScoreController extends BaseController
{
    protected $semesterScoreService;
    protected $db;
    protected $configModel;
    protected $semester;
    protected $schoolYear;
    protected $classSectionModel;
    protected $studentClassModel;
    protected $teacherClassModel;
    protected $userModel;
    protected $targetHigh;
    protected $targetLow;
    protected $semesterScoreModel;
    protected $studentModel;
    protected $calendarModel;
    protected $attendanceCalculator;
    protected $gradingLockModel;
    protected $missingScoreOverrideModel;

    public function __construct()
    {
                // Load the database service
        $this->db = \Config\Database::connect();
        // Check if the database connection is established
        if (!$this->db->connect()) {
            log_message('error', 'Database connection failed.');
            throw new \Exception('Database connection failed.');
        } else {
            log_message('info', 'Database connection successful.');
        }

        // Initialize the config model
        $this->configModel = new ConfigurationModel(); 
        $this->classSectionModel = new ClassSectionModel();
        $this->teacherClassModel = new TeacherClassModel();
        $this->semesterScoreModel = new SemesterScoreModel();
        $this->studentModel = new StudentModel();
        $this->studentClassModel = new StudentClassModel();
        $this->userModel = new UserModel();
        $this->calendarModel = new CalendarModel();
        $this->attendanceCalculator = new AttendanceCalculator(
            new AttendanceRecordModel(),
            $this->configModel,
            $this->calendarModel
        );
        $this->gradingLockModel = new GradingLockModel();
        $this->missingScoreOverrideModel = new MissingScoreOverrideModel();
        // Retrieve the configuration values
        $this->semester = $this->configModel->getConfig('semester');
        $this->schoolYear = $this->configModel->getConfig('school_year');
        $this->targetHigh = $this->configModel->getConfig('trophy_score');
        $this->targetLow = $this->configModel->getConfig('pass_score');
        $this->semesterScoreService = Services::semesterScoreService();
    }


    public function index()
    {
        log_message('debug', 'ScoreController::index invoked');
        $schoolYearContext = $this->resolveSchoolYearContext();
        $this->schoolYear = $schoolYearContext->yearName();
        $semesterChoiceParam = $this->request->getGet('semester_choice');
        $session = session();
        if ($this->request->getGet('choose_semester') !== null) {
            $session->remove('teacher_scores_selected_semester');
            $session->remove('semester');
            $semesterChoiceParam = null;
        }
        $normalizedChoice = $this->normalizeSemester($semesterChoiceParam);
        $sessionSelected = $session->get('teacher_scores_selected_semester');
        $selectedSemesterLabel = '';
        if ($normalizedChoice !== '') {
            $selectedSemesterLabel = ucfirst($normalizedChoice);
            $session->set('teacher_scores_selected_semester', $selectedSemesterLabel);
            $session->set('semester', $selectedSemesterLabel);
        } elseif (!empty($sessionSelected)) {
            $selectedSemesterLabel = $sessionSelected;
        } else {
            $invalidChoice = null;
            if ($semesterChoiceParam !== null && $semesterChoiceParam !== '') {
                $invalidChoice = $semesterChoiceParam;
            }
            return view('teacher/select_semester', [
                'fallUrl' => base_url('/teacher/scores?semester_choice=Fall'),
                'springUrl' => base_url('/teacher/scores?semester_choice=Spring'),
                'invalidChoice' => $invalidChoice,
            ]);
        }
        $focusTarget = $this->request->getGet('focus');
        $focusTarget = in_array($focusTarget, ['scores', 'comments'], true) ? $focusTarget : null;

        // Step 1: Get the teacher_id from the session
        $teacherId = session()->get('user_id');
        $teacher = $this->userModel->find($teacherId);
        $teacherFirst = $teacher['firstname'] ?? $teacher['first_name'] ?? '';
        $teacherLast  = $teacher['lastname']  ?? $teacher['last_name']  ?? '';
        $teacherName  = ($teacherFirst !== '' || $teacherLast !== '')
            ? trim($teacherFirst . ' ' . $teacherLast)
            : 'Unknown';

        $activeSemester = $selectedSemesterLabel;
        $effectiveSemester = $activeSemester;
        $effectiveSchoolYear = $this->schoolYear;

        // ✅ Get class section ID for the active term (respect active selection)
        $assignments = $this->teacherClassModel->getClassAssignmentsByUserId(
            (int)$teacherId,
            (string)$effectiveSchoolYear,
            (string)$effectiveSemester
        );

        if (empty($assignments)) {
            log_message(
                'info',
                "No {$effectiveSchoolYear} {$effectiveSemester} class section assigned to teacher ID: {$teacherId}"
            );
            return redirect()
                ->to('no-classes')
                ->with('message', 'You do not have an assigned class for the selected school year and semester. Please contact the administration.');
        }

        $allowedIds = array_map(static fn($a) => (int)($a['class_section_id'] ?? 0), $assignments);
        $allowedIds = array_values(array_filter(array_unique($allowedIds)));

        $requestedId = (int)($this->request->getGet('class_section_id') ?? 0);
        $sessionId   = (int)(session()->get('class_section_id') ?? 0);
        $chosenAssignment = null;
        if ($requestedId && in_array($requestedId, $allowedIds, true)) {
            $classSectionId = $requestedId;
            foreach ($assignments as $a) {
                if ((int)$a['class_section_id'] === $requestedId) { $chosenAssignment = $a; break; }
            }
        } elseif ($sessionId && in_array($sessionId, $allowedIds, true)) {
            $classSectionId = $sessionId;
            foreach ($assignments as $a) {
                if ((int)$a['class_section_id'] === $sessionId) { $chosenAssignment = $a; break; }
            }
        } else {
            $classSectionId = $allowedIds[0];
            foreach ($assignments as $a) {
                if ((int)$a['class_section_id'] === $classSectionId) { $chosenAssignment = $a; break; }
            }
        }

        session()->set('class_section_id', $classSectionId);

        if ($chosenAssignment) {
            $effectiveSemester   = $chosenAssignment['semester'] ?? $effectiveSemester;
            $effectiveSchoolYear = $chosenAssignment['school_year'] ?? $effectiveSchoolYear;
        }

        // Build assigned teacher/TA names for this section (current term)
        $rows = $this->db->table('teacher_class tc')
            ->select('tc.position, u.firstname, u.lastname')
            ->join('users u', 'u.id = tc.teacher_id', 'inner')
            ->where('tc.class_section_id', $classSectionId)
            ->where('tc.school_year', $effectiveSchoolYear)
            ->orderBy("FIELD(tc.position,'main','ta')", '', false)
            ->orderBy('u.firstname', 'ASC')
            ->get()->getResultArray();

        $mains = [];
        $tas   = [];
        foreach ($rows as $r) {
            $name = trim(($r['firstname'] ?? '') . ' ' . ($r['lastname'] ?? ''));
            if ($name === '') continue;
            $pos = strtolower((string)($r['position'] ?? ''));
            if (in_array($pos, ['main', 'teacher'], true)) {
                $mains[] = $name;
            } elseif (in_array($pos, ['ta', 'assistant', 'teacher_assistant'], true)) {
                $tas[] = $name;
            }
        }
        if (empty($mains) && !empty($teacherName)) {
            $mains = [$teacherName];
        }
        $mainsText = implode(', ', $mains);
        $tasText   = implode(', ', $tas);

        // Resolve class section name (by section code)
        $csRow = $this->db->table('classSection')
            ->select('class_section_name')
            ->where('class_section_id', $classSectionId)
            ->where('school_year', $effectiveSchoolYear)
            ->get()->getRowArray();
        $classSectionName = $csRow['class_section_name'] ?? '';
        log_message('debug', "ScoreController::index teacher {$teacherId} classSection {$classSectionId} semester {$effectiveSemester}");

        $studentTeacherInfo = $this->studentModel->getStudentInfoByClassSectionId(
            $classSectionId,
            $effectiveSemester,
            $effectiveSchoolYear
        );
        if (!empty($studentTeacherInfo) && $this->semesterScoreService !== null) {
            try {
                $this->semesterScoreService->updateScoresForStudents($studentTeacherInfo);
            } catch (\Throwable $e) {
                log_message('error', 'ScoreController::index semester score refresh failed: ' . $e->getMessage());
            }
        }

        // Step 2: Retrieve the student scores data
        $students = $this->getStudentScores($classSectionId, $effectiveSemester, $effectiveSchoolYear, $teacherId);

        // Step 3: Sort the students array by last name and then by first name
        usort($students, function ($a, $b) {
            $lastNameComparison = strcmp($a['lastname'], $b['lastname']);
            return $lastNameComparison === 0
                ? strcmp($a['firstname'], $b['firstname'])
                : $lastNameComparison;
        });

        if (ENVIRONMENT !== 'production') {
            $sampleRows = array_map(static function ($student) {
                return [
                    'id' => $student['student_id'],
                    'attendance_score' => $student['attendance_score'] ?? null,
                    'ptap_score' => $student['ptap_score'] ?? null,
                    'final_exam' => $student['final_exam'][0] ?? null,
                ];
            }, array_slice($students, 0, 5));
            log_message('debug', 'ScoreController::index sample rows: ' . json_encode($sampleRows));
        }

        $scoresLocked = $this->gradingLockModel->isLocked(
            (int) $classSectionId,
            (string) $effectiveSemester,
            (string) $effectiveSchoolYear
        );

        // Step 4: Pass everything to the view
        $data = [
            'students' => $students,
            'teacher_name' => $teacherName,
            'class_section_id' => $classSectionId,
            'class_section_name' => $classSectionName,
            'mainsText' => $mainsText,
            'tasText'   => $tasText,
            'activeSemester' => $activeSemester,
            'selectedSemester' => $activeSemester,
            'focusTarget' => $focusTarget,
            'schoolYear' => $effectiveSchoolYear,
            'scoresLocked' => $scoresLocked,
            'isSchoolYearReadonly' => $schoolYearContext->isReadonly(),
        ];

        return view('/teacher/scores', $data);
    }

    public function submitScoresLock()
    {
        $schoolYearContext = $this->resolveSchoolYearContext();
        $this->assertSchoolYearWritable($schoolYearContext);
        $classSectionId = (int) ($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0);
        $semesterRaw = (string) ($this->request->getPost('semester') ?? $this->request->getPost('selected_semester') ?? '');
        $semesterNormalized = $this->normalizeSemester($semesterRaw);
        $semester = $semesterNormalized !== '' ? ucfirst($semesterNormalized) : $this->semester;
        $schoolYear = $schoolYearContext->yearName();

        if ($classSectionId <= 0 || $semester === '' || $schoolYear === '') {
            return redirect()->back()->with('error', 'Missing class section or term.');
        }

        $missingOk = $this->request->getPost('missing_ok') ?? [];
        $studentIds = $this->request->getPost('student_ids') ?? array_keys((array) $missingOk);
        $studentIds = array_map('intval', (array) $studentIds);
        $teacherId = (int) (session()->get('user_id') ?? 0);
        $isFall = strtolower($semester) === 'fall';
        $isSpring = strtolower($semester) === 'spring';

        $commentTypes = ['ptap_comment'];
        if ($isFall) {
            $commentTypes[] = 'midterm_comment';
        }
        if ($isSpring) {
            $commentTypes[] = 'final_comment';
        }

        foreach ($commentTypes as $type) {
            $checkedItems = [];
            if (is_array($missingOk)) {
                foreach ($missingOk as $studentId => $types) {
                    if (!is_array($types) || empty($types[$type])) {
                        continue;
                    }
                    $checkedItems[] = [
                        'student_id' => (int) $studentId,
                        'item_index' => null,
                    ];
                }
            }
            $this->missingScoreOverrideModel->replaceOverrides(
                $classSectionId,
                (string) $semester,
                (string) $schoolYear,
                $type,
                $studentIds,
                null,
                $checkedItems,
                $teacherId,
                true
            );
        }

        $existing = $this->gradingLockModel->getLock($classSectionId, $semester, $schoolYear);
        if (!empty($existing) && !empty($existing['is_locked'])) {
            return redirect()->back()->with('status', 'Scores already submitted and locked.');
        }

        $userId = $teacherId;
        if (!empty($existing)) {
            $this->gradingLockModel->update($existing['id'], [
                'is_locked' => 1,
                'locked_by' => $userId > 0 ? $userId : null,
                'locked_at' => utc_now(),
            ]);
        } else {
            $this->gradingLockModel->insert([
                'class_section_id' => $classSectionId,
                'semester' => $semester,
                'school_year' => $schoolYear,
                'is_locked' => 1,
                'locked_by' => $userId > 0 ? $userId : null,
                'locked_at' => utc_now(),
            ]);
        }

        return redirect()->back()->with('status', 'Scores submitted and locked for this semester.');
    }

    public function noClasses()
    {
        return view('/teacher/no_classes');
    }

    private function getStudentScores(int $classSectionId, $semester, $schoolYear, int $teacherId)
    {
        if (!$semester || !$schoolYear) {
            throw new \RuntimeException('Semester or school year configuration is missing.');
        }

        $totalSemesterDays = null;
        $normSemester = $this->normalizeSemester($semester);
        if ($schoolYear !== '' && $normSemester !== '') {
            $semRange = $this->getSemesterRange($schoolYear, $normSemester);
            if ($semRange) {
                try {
                    $events = $this->calendarModel->getEventsBySchoolYearAndSemester($schoolYear, $normSemester);
                    $noSchoolSundays = 0;
                    foreach ($events as $event) {
                        if (!empty($event['no_school'])) {
                            $date = new \DateTime($event['date']);
                            if ($date->format('N') == 7) {
                                if ($date->format('Y-m-d') >= $semRange[0] && $date->format('Y-m-d') <= $semRange[1]) {
                                    $noSchoolSundays++;
                                }
                            }
                        }
                    }
                    $totalSemesterDays = $this->countSundays($semRange[0], $semRange[1]) - $noSchoolSundays;
                } catch (\Throwable $e) {
                    log_message('error', 'Failed to count sundays for score calculation: ' . $e->getMessage());
                    $totalSemesterDays = null;
                }
            }
        }

        if ($totalSemesterDays === null) {
            $totalSemesterDays = strtolower($semester) === 'fall'
                ? $this->configModel->getConfig('total_semester1_days')
                : $this->configModel->getConfig('total_semester2_days');
        }


        if (!$totalSemesterDays) {
            throw new \RuntimeException('Total semester days configuration is missing.');
        }

        if ($classSectionId <= 0) {
            throw new \RuntimeException('Invalid class section for the current teacher.');
        }
        $students = $this->prepareStudents($classSectionId, $teacherId, $schoolYear);
        $studentIds = array_keys($students);

        // Load regular scores
        $this->loadScoresByType(new HomeworkModel(), 'homework', $students, $classSectionId, $semester, $schoolYear);
        $this->loadScoresByType(new QuizModel(), 'quiz', $students, $classSectionId, $semester, $schoolYear);
        $this->loadScoresByType(new ProjectModel(), 'project', $students, $classSectionId, $semester, $schoolYear);
        $this->loadScoresByType(new MidtermExamModel(), 'midterm', $students, $classSectionId, $semester, $schoolYear);
        $this->loadScoresByType(new FinalExamModel(), 'final_exam', $students, $classSectionId, $semester, $schoolYear);
        $this->loadScoresByType(new ParticipationModel(), 'participation', $students, $classSectionId, $semester, $schoolYear);

        $this->calculateStudentScores($students, $classSectionId, $semester, $totalSemesterDays);

        // Load all semester scores in one query
        $this->loadSemesterScores($students, $classSectionId, $semester, $schoolYear);

        $this->loadComments(new ScoreCommentModel(), $students, $studentIds, $semester, $schoolYear);

        $homeworkIndexes = $this->loadIndexedScores(new HomeworkModel(), 'homework_index', $students, $classSectionId, $semester, $schoolYear, 'homework_scores_by_index');
        $quizIndexes = $this->loadIndexedScores(new QuizModel(), 'quiz_index', $students, $classSectionId, $semester, $schoolYear, 'quiz_scores_by_index');
        $projectIndexes = $this->loadIndexedScores(new ProjectModel(), 'project_index', $students, $classSectionId, $semester, $schoolYear, 'project_scores_by_index');
        $this->loadMissingOverrides($students, $classSectionId, $semester, $schoolYear);
        $this->buildMissingItems($students, $homeworkIndexes, $quizIndexes, $projectIndexes, $semester);

        return $students;
    }

    private function loadSemesterScores(&$students, $classSectionId, $semester, $schoolYear)
    {
        // Get all scores in one query
        $results = $this->semesterScoreModel->select('student_id, attendance_score, ptap_score, semester_score, midterm_exam_score, final_exam_score')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->findAll();

        foreach ($results as $row) {
            $studentId = $row['student_id'];
            
            if (isset($students[$studentId])) {
                $attendanceScore = $this->roundScore($row['attendance_score'] ?? null);
                if ($attendanceScore !== null && !isset($students[$studentId]['attendance_score'])) {
                    $students[$studentId]['attendance_score'] = $attendanceScore;
                }
                $ptapScore = $this->roundScore($row['ptap_score'] ?? null);
                if ($ptapScore !== null && !isset($students[$studentId]['ptap_score'])) {
                    $students[$studentId]['ptap_score'] = $ptapScore;
                }
                $semesterScore = $this->roundScore($row['semester_score'] ?? null);
                if ($semesterScore !== null && !isset($students[$studentId]['semester_score'])) {
                    $students[$studentId]['semester_score'] = $semesterScore;
                }
                $midtermScore = $this->roundScore($row['midterm_exam_score'] ?? null);
                if ($midtermScore !== null && empty($students[$studentId]['midterm'])) {
                    $students[$studentId]['midterm'][] = $midtermScore;
                }
                $finalExamScore = $this->roundScore($row['final_exam_score'] ?? null);
                if ($finalExamScore !== null && empty($students[$studentId]['final_exam'])) {
                    $students[$studentId]['final_exam'][] = $finalExamScore;
                }
            }
        }
    }

    private function roundScore($value): ?float
    {
        if ($value === null) {
            return null;
        }
        
        // Ensure value is numeric before rounding
        return is_numeric($value) ? round((float)$value, 1) : null;
    }

    private function prepareStudents($classSectionId, $teacherId, $schoolYear)
    {
        $students = [];
        $studentClasses = $this->studentClassModel
            ->active()
            ->where('student_class.class_section_id', $classSectionId)
            ->where('student_class.school_year', $schoolYear)
            ->findAll();

        foreach ($studentClasses as $studentClass) {
            $student = $this->studentModel
                ->select('id, firstname, lastname, age, school_id')
                ->where('id', $studentClass['student_id'])
                ->where('is_active', 1)
                ->first();
            if ($student) {
                $students[$student['id']] = [
                    'student_id' => $student['id'],
                    'firstname' => $student['firstname'],
                    'lastname' => $student['lastname'],
                    'age' => $student['age'],
                    'school_id' => $student['school_id'],
                    'class_section_id' => $classSectionId,
                    'school_year' => $schoolYear,
                    'teacher_id' => $teacherId,
                    'homework' => [],
                    'quiz' => [],
                    'midterm' => [],
                    'final_exam' => [],
                    'project' => [],
                    'participation' => [],
                    'ptap' => [],
                    'ptap_comment' => '',
                    'midterm_comment' => '',
                    'final_comment' => ''
                ];
            }
        }
        return $students;
    }

    private function loadScoresByType($model, $type, &$students, $classSectionId, $semester, $schoolYear)
    {
        $records = $model->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->findAll();

        foreach ($records as $rec) {
            if (!isset($students[$rec['student_id']])) {
                continue;
            }

            // Only include real numeric scores; leave null/blank out entirely
            if ($rec['score'] === null || $rec['score'] === '') {
                continue;
            }

            if (!is_numeric($rec['score'])) {
                continue;
            }

            $students[$rec['student_id']][$type][] = (float) $rec['score'];
        }
    }

    private function loadIndexedScores($model, string $indexField, array &$students, int $classSectionId, $semester, $schoolYear, string $storeKey): array
    {
        $records = $model->select("student_id, {$indexField} as idx, score")
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->findAll();

        $indexes = [];
        foreach ($records as $rec) {
            $sid = (int) ($rec['student_id'] ?? 0);
            $idx = (int) ($rec['idx'] ?? 0);
            if ($idx <= 0) {
                continue;
            }
            $indexes[$idx] = true;
            if (isset($students[$sid])) {
                if (!isset($students[$sid][$storeKey]) || !is_array($students[$sid][$storeKey])) {
                    $students[$sid][$storeKey] = [];
                }
                $students[$sid][$storeKey][$idx] = $rec['score'] ?? null;
            }
        }

        $indexList = array_keys($indexes);
        sort($indexList, SORT_NUMERIC);
        return $indexList;
    }

    private function loadComments($commentModel, &$students, $studentIds, $semester, $schoolYear)
    {
        if (empty($studentIds)) return;

        $semVariants = array_values(array_unique(array_filter([
            $semester,
            strtolower((string)$semester),
            ucfirst(strtolower((string)$semester)),
        ], static fn($v) => $v !== '')));

        $comments = $commentModel->whereIn('student_id', $studentIds)
            ->whereIn('semester', $semVariants)
            ->where('school_year', $schoolYear)
            ->findAll();

        foreach ($comments as $comment) {
            $sid = $comment['student_id'];
            if (!isset($students[$sid])) continue;
            
            $type = strtolower($comment['score_type']);
            
            // Set both comment and comment_review for each type
            if ($type === 'ptap') {
                $students[$sid]['ptap_comment'] = $comment['comment'];
                $students[$sid]['ptap_comment_review'] = $comment['comment_review'] ?? null;
            } 
            elseif ($type === 'midterm') {
                $students[$sid]['midterm_comment'] = $comment['comment'];
                $students[$sid]['midterm_comment_review'] = $comment['comment_review'] ?? null;
            } 
            elseif ($type === 'final') {
                $students[$sid]['final_comment'] = $comment['comment'];
                $students[$sid]['final_comment_review'] = $comment['comment_review'] ?? null;
            }
            elseif ($type === 'attendance' || $type === 'attendance_comment') {
                $students[$sid]['attendance_comment'] = $comment['comment'];
                $students[$sid]['attendance_comment_review'] = $comment['comment_review'] ?? null;
            }
        }

        // Fallback: if no attendance comment saved, derive one from score for display
        helper('attendance_comment');
        foreach ($students as $sid => &$student) {
            if (!isset($student['attendance_comment']) && isset($student['attendance_score'])) {
                $student['attendance_comment'] = attendance_comment_from_score(
                    $student['attendance_score'],
                    $student['firstname'] ?? ''
                );
            }
        }
    }

    private function loadMissingOverrides(array &$students, int $classSectionId, $semester, $schoolYear): void
    {
        if ($classSectionId <= 0 || empty($students)) {
            return;
        }

        $rows = $this->missingScoreOverrideModel
            ->select('student_id, item_type, item_index')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->where('is_allowed', 1)
            ->findAll();

        foreach ($rows as $row) {
            $sid = (int) ($row['student_id'] ?? 0);
            if (!isset($students[$sid])) {
                continue;
            }
            $type = (string) ($row['item_type'] ?? '');
            if ($type === '') {
                continue;
            }
            $idx = isset($row['item_index']) ? (int) $row['item_index'] : 0;
            if (!isset($students[$sid]['missing_ok'])) {
                $students[$sid]['missing_ok'] = [];
            }
            if (!isset($students[$sid]['missing_ok'][$type])) {
                $students[$sid]['missing_ok'][$type] = [];
            }
            $students[$sid]['missing_ok'][$type][$idx] = true;
        }
    }

    private function buildMissingItems(array &$students, array $homeworkIndexes, array $quizIndexes, array $projectIndexes, $semester): void
    {
        $isFall = strtolower((string) $semester) === 'fall';
        $isSpring = strtolower((string) $semester) === 'spring';

        foreach ($students as $sid => &$student) {
            $missing = [];
            $missingOk = $student['missing_ok'] ?? [];
            $isAllowed = static function (array $missingOk, string $type, ?int $idx = null): bool {
                $key = $idx === null ? 0 : $idx;
                return !empty($missingOk[$type][$key]);
            };

            foreach ($homeworkIndexes as $idx) {
                $val = $student['homework_scores_by_index'][$idx] ?? null;
                if (($val === null || $val === '' || !is_numeric($val)) && !$isAllowed($missingOk, 'homework', $idx)) {
                    $missing[] = 'Homework ' . $idx;
                }
            }

            foreach ($quizIndexes as $idx) {
                $val = $student['quiz_scores_by_index'][$idx] ?? null;
                if (($val === null || $val === '' || !is_numeric($val)) && !$isAllowed($missingOk, 'quiz', $idx)) {
                    $missing[] = 'Quiz ' . $idx;
                }
            }

            foreach ($projectIndexes as $idx) {
                $val = $student['project_scores_by_index'][$idx] ?? null;
                if (($val === null || $val === '' || !is_numeric($val)) && !$isAllowed($missingOk, 'project', $idx)) {
                    $missing[] = 'Project ' . $idx;
                }
            }

            if (empty($student['participation']) && !$isAllowed($missingOk, 'participation', 0)) {
                $missing[] = 'Participation';
            }

            if ($isFall && empty($student['midterm']) && !$isAllowed($missingOk, 'midterm_exam', 0)) {
                $missing[] = 'Midterm Exam';
            }

            if ($isSpring && empty($student['final_exam']) && !$isAllowed($missingOk, 'final_exam', 0)) {
                $missing[] = 'Final Exam';
            }

            $ptapComment = trim((string) ($student['ptap_comment'] ?? ''));
            if ($ptapComment === '' && !$isAllowed($missingOk, 'ptap_comment', 0)) {
                $missing[] = 'PTAP Comment';
            }

            if ($isFall) {
                $midtermComment = trim((string) ($student['midterm_comment'] ?? ''));
                if ($midtermComment === '' && !$isAllowed($missingOk, 'midterm_comment', 0)) {
                    $missing[] = 'Midterm Comment';
                }
            }

            if ($isSpring) {
                $finalComment = trim((string) ($student['final_comment'] ?? ''));
                if ($finalComment === '' && !$isAllowed($missingOk, 'final_comment', 0)) {
                    $missing[] = 'Final Comment';
                }
            }

            $student['missing_items'] = $missing;
        }
    }

    private function calculateStudentScores(&$students, int $classSectionId, $semester, $totalDays)
    {
        foreach ($students as $sid => &$s) {
            $attendanceScore = $this->calculateAttendanceScoreForStudent(
                $sid,
                $classSectionId,
                $semester,
                $s['school_year'] ?? '',
                $totalDays
            );
            if ($attendanceScore === null) {
                $attendanceScore = 100.0;
            }
            $attendance = $attendanceScore;

            $homeworkScores = array_values(array_filter(
                $s['homework'],
                static fn($score) => $score !== null && $score !== '' && is_numeric($score)
            ));
            $avgHomework = !empty($homeworkScores) ? array_sum($homeworkScores) / count($homeworkScores) : null;

            $quizScores = array_values(array_filter(
                $s['quiz'],
                static fn($score) => $score !== null && $score !== '' && is_numeric($score)
            ));
            $avgQuiz = !empty($quizScores) ? array_sum($quizScores) / count($quizScores) : null;

            $projectScores = array_values(array_filter(
                $s['project'],
                static fn($score) => $score !== null && $score !== '' && is_numeric($score)
            ));
            $avgProject = !empty($projectScores) ? array_sum($projectScores) / count($projectScores) : null;

            $participationScores = array_values(array_filter(
                $s['participation'],
                static fn($score) => $score !== null && $score !== '' && is_numeric($score)
            ));
            $avgParticipation = !empty($participationScores) ? array_sum($participationScores) / count($participationScores) : null;

            $ptap = $this->calculatePtapScore($avgHomework, $avgQuiz, $avgProject, $avgParticipation);

            $midterm = (!empty($s['midterm']) && $s['midterm'][0] !== null && $s['midterm'][0] !== '')
                ? (float) $s['midterm'][0]
                : null;
            $final = (!empty($s['final_exam']) && $s['final_exam'][0] !== null && $s['final_exam'][0] !== '')
                ? (float) $s['final_exam'][0]
                : null;

            $score = null;
            if ($ptap !== null) {
                $score = 0.2 * $ptap + 0.2 * $attendance;
                if (strtolower($semester) === 'fall') {
                    if ($midterm !== null) {
                        $score += 0.6 * $midterm;
                    } else {
                        $score = null;
                    }
                } else {
                    if ($final !== null) {
                        $score += 0.6 * $final;
                    } else {
                        $score = null;
                    }
                }
            }

            $s['average_homework'] = $avgHomework !== null ? round($avgHomework, 1) : null;
            $s['average_quiz'] = $avgQuiz !== null ? round($avgQuiz, 1) : null;
            $s['average_project'] = $avgProject !== null ? round($avgProject, 1) : null;
            $s['average_participation'] = $avgParticipation !== null ? round($avgParticipation, 1) : null;
            $s['ptap_score'] = $ptap !== null ? round($ptap, 1) : null;
            $s['midterm_score'] = $midterm !== null ? round($midterm, 1) : null;
            $s['final_exam_score'] = $final !== null ? round($final, 1) : null;
            $s['attendance_score'] = round($attendanceScore, 1);
            $s['semester_score'] = $score !== null ? round(min($score, 100), 1) : null;
        }
    }

    private function calculateAttendanceScoreForStudent(
        int $studentId,
        int $classSectionId,
        string $semester,
        string $schoolYear,
        ?int $totalDays
    ): ?float {
        $score = $this->getAttendanceScoreFromCalculator(
            $studentId,
            $semester,
            $schoolYear,
            $classSectionId
        );
        if ($score !== null) {
            return $score;
        }
        return $this->calculateAttendanceScoreFallback(
            $studentId,
            $semester,
            $schoolYear,
            $totalDays
        );
    }

    private function getAttendanceScoreFromCalculator(
        int $studentId,
        string $semester,
        string $schoolYear,
        int $classSectionId
    ): ?float {
        try {
            $result = $this->attendanceCalculator->calculate($studentId, $semester, $schoolYear, $classSectionId);
            $score = $result['attendance_score'] ?? null;
            if ($score === null || $score === '') {
                return null;
            }
            return (float) $score;
        } catch (\Throwable $e) {
            log_message(
                'error',
                'ScoreController::getAttendanceScoreFromCalculator failed for '
                . "student {$studentId}: " . $e->getMessage()
            );
        }
        return null;
    }

    private function calculateAttendanceScoreFallback(
        int $studentId,
        string $semester,
        string $schoolYear,
        ?int $totalDays
    ): ?float {
        if ($totalDays === null || $totalDays <= 0) {
            return 100.0;
        }

        $attendanceDataModel = new AttendanceDataModel();
        $absences = $attendanceDataModel
            ->where('student_id', $studentId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->where('status', 'absent')
            ->countAllResults();

        return max(0, ((($totalDays - $absences) / $totalDays) * 100));
    }

    /**
     * PTAP weighting rules (tests are represented by quiz averages):
     * - No tests and no projects: 50% homework, 50% participation.
     * - No projects: 40% homework, 40% participation, 20% tests.
     * - No tests: 34% homework, 33% participation, 33% projects.
     * - All available: 30% homework, 30% participation, 30% projects, 10% tests.
     */
    private function calculatePtapScore(?float $avgHomework, ?float $avgQuiz, ?float $avgProject, ?float $avgParticipation): ?float
    {
        $hasTests = $avgQuiz !== null;
        $hasProjects = $avgProject !== null;

        if (!$hasTests && !$hasProjects) {
            if ($avgHomework === null || $avgParticipation === null) {
                return null;
            }
            return round(($avgHomework * 0.5) + ($avgParticipation * 0.5), 1);
        }

        if (!$hasProjects) {
            if ($avgHomework === null || $avgParticipation === null || $avgQuiz === null) {
                return null;
            }
            return round(($avgHomework * 0.4) + ($avgParticipation * 0.4) + ($avgQuiz * 0.2), 1);
        }

        if (!$hasTests) {
            if ($avgHomework === null || $avgParticipation === null || $avgProject === null) {
                return null;
            }
            return round(($avgHomework * 0.34) + ($avgParticipation * 0.33) + ($avgProject * 0.33), 1);
        }

        if ($avgHomework === null || $avgParticipation === null || $avgProject === null || $avgQuiz === null) {
            return null;
        }

        return round(
            ($avgHomework * 0.3) + ($avgParticipation * 0.3) + ($avgProject * 0.3) + ($avgQuiz * 0.1),
            1
        );
    }

    private function getHomeworkCount(int $classSectionId, string $semester, string $schoolYear): int
    {
        if ($classSectionId <= 0 || $semester === '' || $schoolYear === '') {
            return 0;
        }

        $builder = $this->db->table('homework')
            ->select('COUNT(DISTINCT homework_index) AS hw_count')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester);

        if ($schoolYear !== '') {
            $builder->where('school_year', $schoolYear);
        }

        $row = $builder->get()->getRow();

        // Fallback: if none found (school_year not populated), drop school_year filter
        if ((!$row || $row->hw_count === null) && $schoolYear !== '') {
            $row = $this->db->table('homework')
                ->select('COUNT(DISTINCT homework_index) AS hw_count')
                ->where('class_section_id', $classSectionId)
                ->where('semester', $semester)
                ->get()
                ->getRow();
        }

        return ($row && $row->hw_count !== null) ? (int) $row->hw_count : 0;
    }

    private function saveFinalScores($students, $model, $teacherId, $classSectionId, $semester, $schoolYear)
    {
        foreach ($students as $s) {
            $existing = $model->where('student_id', $s['student_id'])
                ->where('class_section_id', $classSectionId)
                ->where('semester', $semester)
                ->where('school_year', $schoolYear)
                ->first();

            $data = [
                'student_id' => $s['student_id'],
                'class_section_id' => $classSectionId,
                'updated_by' => $teacherId,
                'score' => $s['semester_score'],
                'score_letter' => $this->convertScoreToLetter($s['semester_score']),
                'semester' => $semester,
                'school_year' => $schoolYear,
                'updated_at' => utc_now(),
            ];

            if ($existing) $model->update($existing['id'], $data);
            else {
                $data['created_at'] = utc_now();
                $model->save($data);
            }
        }
    }




    private function convertScoreToLetter($score)
    {
        // Example conversion function, adjust logic as needed
        if ($score >= 90) {
            return 'A';
        } elseif ($score >= 80) {
            return 'B';
        } elseif ($score >= 70) {
            return 'C';
        } elseif ($score >= 60) {
            return 'D';
        } else {
            return 'F';
        }
    }

    private function resolveActiveSemester(): string
    {
        $session = session();
        $selected = $session->get('teacher_scores_selected_semester');
        if (!empty($selected)) {
            return (string) $selected;
        }
        return $session->get('semester') ?? $this->semester;
    }

    protected function normalizeSemester(?string $input): string
    {
        $s = strtolower(trim((string)$input));
        if ($s === 'fall' || str_contains($s, '1')) return 'fall';
        if ($s === 'spring' || str_contains($s, '2')) return 'spring';
        return '';
    }

    protected function getSemesterRange(string $schoolYear, string $semester): ?array
    {
        $norm = $this->normalizeSemester($semester);
        if ($norm === '' || !preg_match('/^(\\d{4})/', trim($schoolYear), $m)) {
            return null;
        }
        $y1 = (int)$m[1];
        $y2 = $y1 + 1;

        $fallStartCfg   = (string)($this->configModel->getConfig('fall_semester_start')   ?? '');
        $fallEndCfg     = (string)($this->configModel->getConfig('fall_end_date')     ?? ''); // e.g., 'YYYY-01-15'
        $springStartCfg = (string)($this->configModel->getConfig('spring_semester_start') ?? ''); // e.g., 'YYYY-01-16'
        $springEndCfg   = (string)($this->configModel->getConfig('last_school_day')   ?? '');

        if ($norm === 'fall') {
            $start = ($fallStartCfg !== '') ? date($y1 . '-m-d', strtotime($fallStartCfg)) : "{$y1}-09-01";
            $end   = ($fallEndCfg   !== '') ? date($y2 . '-m-d', strtotime($fallEndCfg))   : "{$y2}-01-15";
            return [$start, $end];
        }

        if ($norm === 'spring') {
            $start = ($springStartCfg !== '') ? date($y2 . '-m-d', strtotime($springStartCfg)) : "{$y2}-01-16";
            $end   = ($springEndCfg   !== '') ? date($y2 . '-m-d', strtotime($springEndCfg))   : "{$y2}-06-30";
            return [$start, $end];
        }

        return null;
    }

    private function countSundays(string $startDate, string $endDate): int
    {
        $start = new \DateTime($startDate);
        $end = new \DateTime($endDate);
        $sundays = 0;
        $interval = new \DateInterval('P1D');
        $period = new \DatePeriod($start, $interval, $end->modify('+1 day')); // include end date

        foreach ($period as $day) {
            if ($day->format('N') == 7) {
                $sundays++;
            }
        }
        return $sundays;
    }


    public function updateFinalExamScores()
    {
        return $this->updateScores('final_exam');
    }

    public function updateFinalExamScoresForGrading()
    {
        return $this->updateScores('final_exam', base_url('grading'));
    }

    public function updateMidtermScores()
    {
        return $this->updateScores('midterm_exam');
    }

    public function updateScores(string $table, ?string $redirectUrl = null)
    {
        $schoolYearContext = $this->resolveSchoolYearContext();
        $this->assertSchoolYearWritable($schoolYearContext);
        $this->schoolYear = $schoolYearContext->yearName();
        $builder = $this->db->table($table);

        $scores = $this->request->getPost('final_score');  // name must match input form
        $teacherId = session()->get('user_id');
        $classSectionId = session()->get('class_section_id');
        $semester = $this->resolveActiveSemester();
        $affectedStudents = [];

        $classSectionId = (int) ($classSectionId ?? 0);
        if ($classSectionId > 0 && $this->gradingLockModel->isLocked($classSectionId, $semester, $this->schoolYear)) {
            return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.');
        }

        if (is_array($scores) && in_array($table, ['midterm_exam', 'final_exam'], true)) {
            $missingOk = $this->request->getPost('missing_ok') ?? [];
            $studentIds = array_keys($scores);
            $checkedItems = [];
            if (is_array($missingOk)) {
                foreach ($missingOk as $studentId => $flags) {
                    $checkedItems[] = [
                        'student_id' => (int) $studentId,
                        'item_index' => null,
                    ];
                }
            }
            $this->missingScoreOverrideModel->replaceOverrides(
                $classSectionId,
                (string) $semester,
                (string) $this->schoolYear,
                $table === 'midterm_exam' ? 'midterm_exam' : 'final_exam',
                $studentIds,
                null,
                $checkedItems,
                $teacherId,
                true
            );
        }

        if (is_array($scores)) {
            foreach ($scores as $studentId => $data) {
                if (!is_numeric($studentId)) {
                    continue;
                }

                $rawScore = $data['score'] ?? null;
                $normalizedScore = is_numeric($rawScore) ? (float) $rawScore : null;

                // Check if a record already exists for this student
                $existing = $builder
                    ->where('student_id', $studentId)
                    ->where('class_section_id', $classSectionId)
                    ->where('semester', $semester)
                    ->where('school_year', $this->schoolYear)
                    ->get()
                    ->getRow();

                $insertData = [
                    'student_id' => $studentId,
                    'class_section_id' => $classSectionId,
                    'updated_by' => $teacherId,
                'score' => $normalizedScore,
                //'comment' => $data['comment'] ?? null,
                    'semester' => $semester,
                'school_year' => $this->schoolYear,
                'updated_at' => utc_now()
            ];

                if (strcasecmp($table, 'quiz') === 0) {
                    $insertData['quiz_index'] = $data['quiz_index'] ?? null;
                }

                if ($existing) {
                    // Update existing
                    $builder->where('id', $existing->id)->update($insertData);
                } else {
                    // Insert new
                    $insertData['created_at'] = utc_now();
                    $builder->insert($insertData);
                }

                // Track student for events (avoid duplicates)
                $affectedStudents[(int)$studentId] = true;
            }
        }

        // If final exam scores updated during Spring, trigger finalScoreReleased for these students
        if (strcasecmp($table, 'final_exam') === 0 && strcasecmp((string)$this->semester, 'Spring') === 0 && !empty($affectedStudents)) {
            $ids = array_keys($affectedStudents);
            $payload = ['students' => array_map(static fn($sid) => ['id' => (int)$sid], $ids)];
            //Events::trigger('finalScoreReleased', $payload);
        }

        // Ensure semester_scores are re-calculated when midterm/final exams change.
        if (in_array(strtolower($table), ['final_exam', 'midterm_exam'], true) && $this->semesterScoreService !== null) {
            try {
                $studentTeacherInfo = $this->studentModel->getStudentInfoByClassSectionId(
                    $classSectionId,
                    $semester,
                    $this->schoolYear
                );
                if (!empty($studentTeacherInfo)) {
                    $this->semesterScoreService->updateScoresForStudents($studentTeacherInfo, $semester, $this->schoolYear);
                }
            } catch (\Throwable $e) {
                log_message('error', 'semester score resync failed after exam update: ' . $e->getMessage());
            }
        }

        $statusMessage = ucfirst($table) . ' scores updated successfully.';
        if ($redirectUrl !== null) {
            return redirect()->to($redirectUrl)->with('status', $statusMessage);
        }
        return redirect()->back()->with('status', $statusMessage);
    }


    private function fetchAndOrganizeScores($db, &$organizedScores, $tableName, $parentId, $selectedYear, $selectedSemester)
    {
        // Only supports semester_scores table in this context
        if ($tableName !== 'semester_scores') {
            return;
        }

        // Define the columns to select
        $columns = [
            'students.id as student_id',
            'students.school_id',
            'students.firstname as student_firstname',
            'students.lastname as student_lastname',
            'classSection.class_section_name',
            "$tableName.homework_avg",
            "$tableName.quiz_avg",
            "$tableName.project_avg",
            "$tableName.midterm_exam_score",
            "$tableName.final_exam_score",
            "$tableName.attendance_score",
            "$tableName.test_avg",
            "$tableName.semester_score",
            "$tableName.school_year",
            "$tableName.semester",
            "$tableName.created_at",
            "$tableName.updated_at"
            //  "$tableName.comment"
        ];


        // Build the query
        $builder = $db->table($tableName);
        $builder->select(implode(', ', $columns));
        $builder->join('students', 'students.id = ' . $tableName . '.student_id');
        // Join on the correct foreign key: classSection.class_section_id
        $builder->join('classSection', 'classSection.class_section_id = ' . $tableName . '.class_section_id');
        $builder->where('students.parent_id', $parentId)
            ->where("$tableName.school_year", $selectedYear)
            ->where("$tableName.semester", $selectedSemester);

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

        foreach ($scores as $score) {
            if (!isset($score['school_id'])) {
                log_message('error', "Missing school_id for student ID: " . ($score['student_id'] ?? 'unknown'));
                continue;
            }

            $schoolYear = $score['school_year'];
            $semester = $score['semester'];
            $studentSchoolId = $score['school_id'];

            if (!isset($organizedScores[$schoolYear][$semester][$studentSchoolId])) {
                $organizedScores[$schoolYear][$semester][$studentSchoolId] = [
                    'student_firstname'     => $score['student_firstname'],
                    'student_lastname'      => $score['student_lastname'],
                    'class_section_name'    => $score['class_section_name'],
                    'school_id'     => $studentSchoolId, // ✅ Add this line
                    'scores'                => [],
                    // 'comment'               => $score['comment'] ?? ''
                ];
            }


            // Organize each category
            $organizedScores[$schoolYear][$semester][$studentSchoolId]['scores'] = [
                'homework'     => ['score' => $score['homework_avg']],
                'quiz'         => ['score' => $score['quiz_avg']],
                'project'      => ['score' => $score['project_avg']],
                'midterm_exam' => ['score' => $score['midterm_exam_score']],
                'final_exam'   => ['score' => $score['final_exam_score']],
                'attendance'   => ['score' => $score['attendance_score']],
                'test_avg'     => ['score' => $score['test_avg']],
                'semester'     => ['score' => $score['semester_score']],
            ];

            // Optionally include comment
            $organizedScores[$schoolYear][$semester][$studentSchoolId]['comment'] = $score['comment'] ?? '';
        }
    }


public function viewStudentScore()
{
    $parentId = session()->get('user_id');
    $userType = session()->get('user_type') ?? '';
    $firstParentId = null;
    $releaseFall = $this->getParentScoresReleasedForSemester('Fall');
    $releaseSpring = $this->getParentScoresReleasedForSemester('Spring');
    $releaseAny = $releaseFall || $releaseSpring;

    // Identify the firstparent based on user type
    if ($userType === 'primary' || $userType === '') {
        $firstParentId = $parentId;
    } elseif ($userType === 'secondary') {
        $parentData = $this->db->table('parents')
            ->select('parent_id')
            ->where('secondparent_user_id', $parentId)
            ->get()
            ->getRowArray();

        if ($parentData) {
            $firstParentId = $parentData['parent_id'];
        }
    } elseif ($userType === 'tertiary') {
        $authUserData = $this->db->table('authorized_users')
            ->select('user_id as parent_id')
            ->where('authorized_user_id', $parentId)
            ->get()
            ->getRowArray();

        if ($authUserData) {
            $firstParentId = $authUserData['parent_id'];
        }
    }

    if (!$firstParentId) {
        return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.');
    }

    $selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));

    // Initialize scores array
    $scores = [];

    // First get all students with their class section info for this parent
$students = $this->db->table('students')
    ->select('students.id, students.school_id, students.firstname, students.lastname, classSection.class_section_name AS class_section_name')
    ->join('student_class', 'student_class.student_id = students.id', 'left')
    // Join using class_section_id FK stored in student_class
    ->join('classSection', 'classSection.class_section_id = student_class.class_section_id', 'left')
    ->where('students.parent_id', $firstParentId)
    ->get()
    ->getResultArray();

    if (!empty($students)) {
        $studentIdList = array_column($students, 'id');

        // Fetch data from semester_scores for these students
        $semesterScores = $this->db->table('semester_scores')
            ->whereIn('student_id', $studentIdList)
            ->where('school_year', $selectedYear)
            ->get()
            ->getResultArray();

        $scoresByStudent = [];
        foreach ($semesterScores as $score) {
            $studentIdKey = (int)($score['student_id'] ?? 0);
            $semesterName = trim((string)($score['semester'] ?? ''));
            if ($studentIdKey === 0 || $semesterName === '') {
                continue;
            }
            $normalizedSemester = ucfirst(strtolower($semesterName));
            $scoresByStudent[$studentIdKey][$normalizedSemester] = $score;
        }

        foreach ($students as $student) {
            $studentId = (int)$student['id'];
            if (empty($scoresByStudent[$studentId])) {
                continue;
            }
            foreach ($scoresByStudent[$studentId] as $semester => $score) {
                $releaseScores = (strcasecmp($semester, 'Fall') === 0) ? $releaseFall
                    : ((strcasecmp($semester, 'Spring') === 0) ? $releaseSpring : $releaseAny);
                $scores[$selectedYear][$semester][$studentId] = [
                    'student_id' => $studentId,
                    'school_id' => $student['school_id'],
                    'student_firstname' => $student['firstname'],
                    'student_lastname' => $student['lastname'],
                    'class_section_name' => $student['class_section_name'] ?? 'Unknown Class',
                    'comment' => $score['comment'] ?? null,
                    'scores' => [
                        'homework' => ['score' => $score['homework_avg'] ?? '-'],
                        'quiz' => ['score' => $score['quiz_avg'] ?? '-'],
                        'project' => ['score' => $score['project_avg'] ?? '-'],
                        'attendance' => ['score' => $score['attendance_score'] ?? '-'],
                        'participation_score' => ['score' => $score['participation_score'] ?? '-'],
                        'ptap' => ['score' => $releaseScores ? ($score['ptap_score'] ?? '-') : '-'],
                        'midterm_exam' => ['score' => $releaseScores ? ($score['midterm_exam_score'] ?? '-') : '-'],
                        'final_exam' => ['score' => $releaseScores ? ($score['final_exam_score'] ?? '-') : '-'],
                        'semester' => ['score' => $releaseScores ? ($score['semester_score'] ?? '-') : '-']
                    ]
                ];
            }
        }
    }

    return view('/parent/scores', [
        'organizedScores' => $scores,
        'selectedYear' => $selectedYear,
        'semesterOrder' => ['Fall', 'Spring'],
        'showExamScores' => $releaseAny,
        'showExamScoresBySemester' => [
            'Fall' => $releaseFall,
            'Spring' => $releaseSpring,
        ],
    ]);
}

private function getParentScoresReleasedForSemester(string $semester): bool
{
    $key = $this->getParentReleaseKey($semester);
    $raw = $key ? $this->configModel->getConfig($key) : null;
    $raw = (string) ($raw ?? '');
    return in_array(strtolower(trim($raw)), ['1', 'true', 'yes', 'y', 'on'], true);
}

private function getParentReleaseKey(string $semester): ?string
{
    $s = strtolower(trim($semester));
    if ($s === 'fall') {
        return 'parent_scores_released_fall';
    }
    if ($s === 'spring') {
        return 'parent_scores_released_spring';
    }
    return null;
}



}
