<?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\UserModel;
use App\Models\ConfigurationModel;
use App\Controllers\View\GradingController;
use RuntimeException;
use App\Services\SemesterScoreService;
use Config\Services;
use App\Models\GradingLockModel;
use App\Models\MissingScoreOverrideModel;

class QuizController extends BaseController
{
    protected $db;
    protected $semesterScoreService;
    protected $quizModel;
    protected $configModel;
    protected $homeworkModel;
    protected $userModel;
    protected $schoolYear;
    protected $semester;
    protected $studentClassModel;
    protected $studentModel;
    protected $teacherClassModel;
    protected $gradingLockModel;
    protected $missingScoreOverrideModel;

    public function __construct()
    {
        $this->db = \Config\Database::connect();
        $this->teacherClassModel = new TeacherClassModel();
        $this->homeworkModel = new HomeworkModel();
        $this->userModel = new UserModel();
        $this->studentClassModel = new StudentClassModel();
        $this->studentModel      = new StudentModel();
        $this->configModel = new ConfigurationModel();
        $this->quizModel         = new QuizModel();
        $this->semesterScoreService = service('semesterScoreService');
        $this->schoolYear = $this->configModel->getConfig('school_year');
        $this->semester = $this->configModel->getConfig('semester');
        $this->gradingLockModel = new GradingLockModel();
        $this->missingScoreOverrideModel = new MissingScoreOverrideModel();
    }

    public function updateQuizScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null)
    {
        $scores = $this->request->getPost('scores');
        log_message('error', '✅ Raw Scores: ' . print_r($scores, true));

        if ($updatedBy === null) {
            $updatedBy = session()->get('user_id');
        }
        $classSectionId = $this->request->getPost('class_section_id') ?? session()->get('class_section_id');
        $semester = $this->request->getPost('semester') ?? $this->getTeacherSelectedSemester();
        $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
        $this->assertSchoolYearWritable($this->resolveSchoolYearContext());
        $classSectionId = (int) ($classSectionId ?? 0);
        if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) {
            return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.');
        }

        $missingOk = $this->request->getPost('missing_ok') ?? [];
        $studentIds = is_array($scores) ? array_keys($scores) : array_keys((array) $missingOk);
        $indexSet = [];
        if (is_array($scores)) {
            foreach ($scores as $studentId => $quizData) {
                if (!is_array($quizData)) {
                    continue;
                }
                foreach ($quizData as $index => $score) {
                    if (is_numeric($index)) {
                        $indexSet[(int)$index] = true;
                    }
                }
            }
        }
        $checkedItems = [];
        if (is_array($missingOk)) {
            foreach ($missingOk as $studentId => $indexes) {
                if (!is_array($indexes)) {
                    continue;
                }
                foreach ($indexes as $index => $flag) {
                    if (!is_numeric($index)) {
                        continue;
                    }
                    $checkedItems[] = [
                        'student_id' => (int) $studentId,
                        'item_index' => (int) $index,
                    ];
                }
            }
        }
        $this->missingScoreOverrideModel->replaceOverrides(
            $classSectionId,
            (string) $semester,
            (string) $schoolYear,
            'quiz',
            $studentIds,
            array_keys($indexSet),
            $checkedItems,
            $updatedBy
        );

        if (is_array($scores)) {
            foreach ($scores as $studentId => $quizData) {
                if (!is_numeric($studentId) || $studentId <= 0) continue;

                // Normalize: if single input submitted
                if (!is_array($quizData)) {
                    $quizData = [1 => $quizData];
                }

                foreach ($quizData as $quizNumber => $score) {
                    if (!is_numeric($quizNumber)) continue;

                    $isBlank = $score === null || (is_string($score) && trim($score) === '');
                    $normalizedScore = (!$isBlank && is_numeric($score)) ? (float) $score : null;

                    $existing = $this->quizModel->where([
                        'student_id'       => $studentId,
                        'quiz_index'      => $quizNumber,
                        'class_section_id' => $classSectionId,
                        'semester'         => $semester,
                        'school_year'      => $schoolYear,
                    ])->first();

                    if ($isBlank) {
                        if ($existing) {
                            $this->quizModel->update($existing['id'], [
                                'score'      => null,
                                'updated_at' => utc_now(),
                            ]);
                        } else {
                            $this->quizModel->insert([
                                'student_id'       => $studentId,
                                'school_id'        => '', // Optional
                                'class_section_id' => $classSectionId,
                                'updated_by'       => $updatedBy,
                                'quiz_index'      => $quizNumber,
                                'score'            => null,
                                'semester'         => $semester,
                                'school_year'      => $schoolYear,
                                'created_at'       => utc_now(),
                                'updated_at'       => utc_now()
                            ]);
                        }
                        continue;
                    }

                    if ($existing) {
                        $this->quizModel->update($existing['id'], [
                            'score'      => $normalizedScore,
                            'updated_at' => utc_now()
                        ]);
                        log_message('debug', "✅ Updated quiz ID {$existing['id']} with score {$normalizedScore}");
                    } else {
                        $this->quizModel->insert([
                            'student_id'       => $studentId,
                            'school_id'        => '', // Optional
                            'class_section_id' => $classSectionId,
                            'updated_by'       => $updatedBy,
                            'quiz_index'      => $quizNumber,
                            'score'            => $normalizedScore,
                        'semester'         => $semester,
                        'school_year'      => $schoolYear,
                            'created_at'       => utc_now(),
                            'updated_at'       => utc_now()
                        ]);
                        log_message('debug', "✅ Inserted quiz for student $studentId quiz $quizNumber with score $score");
                    }
                }
            }
        }

        $studentTeacherInfo = $this->studentModel->getStudentInfoByClassSectionId($classSectionId, $semester, $schoolYear);
        // Call the updateScoresForStudents method
        try {

            $this->semesterScoreService->updateScoresForStudents($studentTeacherInfo, $semester, $schoolYear);
        } catch (RuntimeException $e) {
            // Handle error
        }
        return redirect()->to(base_url('/teacher/addQuiz'))->with('status', 'Quiz scores updated successfully.');
    }

    public function addQuiz()
    {
        // 1) class_section_id from view (POST/GET) with session fallback
        $incoming = $this->request->getPost('class_section_id')
            ?? $this->request->getGet('class_section_id')
            ?? session()->get('class_section_id');

        $classSectionId = (int) ($incoming ?: 0);
        if ($classSectionId <= 0) {
            return redirect()->back()->with('status', 'Missing class section.');
        }
        session()->set('class_section_id', $classSectionId);

        // 3) Headers: distinct quiz_index for this class/term
        $semester = $this->getTeacherSelectedSemester();
        $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
        $quizHeaderRows = $this->quizModel->select('quiz_index')
            ->where('class_section_id', $classSectionId)
            ->where('school_year', $schoolYear)
            ->where('semester', $semester)
            ->groupBy('quiz_index')
            ->orderBy('quiz_index', 'ASC')
            ->get()->getResultArray();

        $quizHeaders = array_map(static fn($r) => (int)$r['quiz_index'], $quizHeaderRows); // [1,2,3,...]

        // 4) Roster (distinct students for this section/term)
        $roster = $this->db->table('student_class sc')
            ->select('s.id AS student_id, s.firstname, s.lastname, s.school_id')
            ->distinct() // ✅ apply DISTINCT correctly
            ->join('students s', 's.id = sc.student_id', 'inner')
            ->where('sc.class_section_id', $classSectionId)
            ->where('sc.school_year', $schoolYear)
            ->orderBy('s.lastname', 'ASC')
            ->orderBy('s.firstname', 'ASC')
            ->get()->getResultArray();


        // 5) All existing quiz scores (single query) -> pivot
        $scoreRows = $this->quizModel->select('id, student_id, quiz_index, score')
            ->where('class_section_id', $classSectionId)
            ->where('school_year', $schoolYear)
            ->where('semester', $semester)
            ->get()->getResultArray();

        $scoresMap = []; // [student_id][quiz_index] => score
        $idMap     = []; // [student_id][quiz_index] => id (if you need to update existing rows)

        foreach ($scoreRows as $r) {
            $sid = (int)$r['student_id'];
            $qi  = (int)$r['quiz_index'];
            $scoresMap[$sid][$qi] = $r['score'];
            $idMap[$sid][$qi]     = (int)$r['id'];
        }

        // 6) Build students array with per-quiz scores
        $students = [];
        foreach ($roster as $st) {
            $sid = (int)$st['student_id'];

            $quizScores = [];
            foreach ($quizHeaders as $qi) {
                $quizScores[$qi] = $scoresMap[$sid][$qi] ?? ''; // empty if none yet
            }

            $students[] = [
                'student_id' => $sid,
                'firstname'  => $st['firstname'],
                'lastname'   => $st['lastname'],
                'school_id'  => $st['school_id'] ?? null,
                'scores'     => $quizScores,
                // If your view needs existing row ids to decide update vs insert:
                'existingIds' => $idMap[$sid] ?? [], // optional
            ];
        }

        // 7) Render
        return view('teacher/add_quiz', [
            'students'       => $students,
            'quizHeaders'    => $quizHeaders,  // e.g., [1,2,3]
            'semester'       => $semester,
            'schoolYear'     => $schoolYear,
            'classSectionId' => $classSectionId,
            'missingOkMap'   => $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $schoolYear, 'quiz'),
        ]);
    }


    public function addNextQuizColumn()
    {
        $updatedBy = session()->get('user_id');
        $classSectionId = session()->get('class_section_id');

        if (!$updatedBy || !$classSectionId) {
            return $this->response->setJSON([
                'status'  => 'error',
                'message' => 'Missing teacher or class section data.'
            ]);
        }

        try {

        $semester = $this->getTeacherSelectedSemester();
        $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
        $this->assertSchoolYearWritable($this->resolveSchoolYearContext());
        $existingQuizNumbers = $this->quizModel
            ->select('quiz_index')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
                ->groupBy('quiz_index')
                ->orderBy('quiz_index', 'DESC')
                ->findAll();

            $maxQuizNumber = 0;
            foreach ($existingQuizNumbers as $row) {
                if (is_numeric($row['quiz_index'])) {
                    $maxQuizNumber = max($maxQuizNumber, (int)$row['quiz_index']);
                }
            }

            $nextQuizNumber = $maxQuizNumber + 1;

            if ($nextQuizNumber <= 0) {
                return $this->response->setJSON([
                    'status'     => 'error',
                    'message'    => '🚫 Invalid quiz number',
                    'calculated' => $nextQuizNumber
                ]);
            }

            $students = $this->studentClassModel
                ->where('class_section_id', $classSectionId)
                ->where('school_year', $schoolYear)
                ->findAll();

            if (empty($students)) {
                return $this->response->setJSON([
                    'status'  => 'error',
                    'message' => 'No students found in this class section.'
                ]);
            }

            $firstInsertedId = null;

            foreach ($students as $i => $student) {
                $exists = $this->quizModel
                    ->where([
                        'student_id'       => $student['student_id'],
                        'quiz_index'      => $nextQuizNumber,
                        'class_section_id' => $classSectionId,
                        'updated_by'       => $updatedBy,
                        'semester'         => $semester,
                        'school_year'      => $schoolYear,
                    ])
                    ->first();

                if ($exists) continue;

                $insertData = [
                    'student_id'       => $student['student_id'],
                    'class_section_id' => $classSectionId,
                    'updated_by'       => $updatedBy,
                    'quiz_index'      => $nextQuizNumber,
                    'score'            => null,
                    'semester'         => $semester,
                    'school_year'      => $schoolYear,
                    'created_at'       => utc_now(),
                    'updated_at'       => utc_now(),
                ];

                log_message('error', '✅ Inserting quiz row: ' . json_encode($insertData));

                try {
                    $id = $this->quizModel->insert($insertData, true);
                    if ($i === 0) $firstInsertedId = $id;
                } catch (\Throwable $e) {
                    return $this->response->setJSON([
                        'status'  => 'error',
                        'message' => '❌ Insert failed: ' . $e->getMessage(),
                        'debug'   => $insertData
                    ]);
                }
            }

            return $this->response->setJSON([
                'status'        => 'success',
                'quiz_index'   => $nextQuizNumber,
                'new_quiz_id'   => $firstInsertedId
            ]);
        } catch (\Throwable $e) {
            return $this->response->setJSON([
                'status'  => 'error',
                'message' => '❌ Unexpected error: ' . $e->getMessage()
            ]);
        }
    }

    public function showQuizMngt()
    {
        // Accept POST first, then GET (query string), then session fallback
        $incomingId = $this->request->getPost('class_section_id');
        if (!$incomingId) {
            $incomingId = $this->request->getGet('class_section_id');
        }

        $classSectionId = (int) ($incomingId ?: (session()->get('class_section_id') ?? 0));

        if ($classSectionId <= 0) {
            return redirect()->back()->with('status', 'No class section found for the current teacher or selection.');
        }
        session()->set('class_section_id', $classSectionId);
        $semester = $this->getTeacherSelectedSemester();
        $schoolYear = $this->schoolYear;
        $quizScores = $this->getquizScoresByStudent($classSectionId, $semester, $schoolYear);
        $students = $this->getStudentsByClassSectionAndYear($classSectionId, $schoolYear);
        $quizHeaders = $this->getquizHeaders($classSectionId, $semester, $schoolYear);
        $scoresLocked = $this->isScoresLocked($classSectionId, $semester, $schoolYear);

        return view('grading/quiz', [
            'quizScores'    => $quizScores,
            'students'          => $students,
            'semester'          => $semester,
            'schoolYear'        => $schoolYear,
            'quizHeaders'   => $quizHeaders,
            'classSectionId' => $classSectionId,
            'scoresLocked'   => $scoresLocked,
        ]);
    }

    public function updateQuiz()
    {
        $scores       = $this->request->getPost('scores');
        $studentIds   = $this->request->getPost('student_ids');
        $semester     = $this->request->getPost('semester') ?? $this->getTeacherSelectedSemester();
        $schoolYear   = $this->request->getPost('school_year') ?? $this->schoolYear;

        $updatedBy = $this->request->getPost('teacher_id') ?? session()->get('user_id');
        $classSectionId = $this->request->getPost('class_section_id') ?? session()->get('class_section_id'); // or query it
        $classSectionId = (int) ($classSectionId ?? 0);
        if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) {
            return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.');
        }

        if ($scores && $studentIds && $semester && $schoolYear && $classSectionId) {
            $this->updateQuizScores($scores, $updatedBy, $classSectionId);
            session()->setFlashdata('status', 'quiz scores updated successfully.');

            // ⬇️ Directly call the method and return its view
            return $this->showQuizMngt();
        }

        $studentTeacherInfo = $this->studentModel->getStudentInfoByClassSectionId($classSectionId, $semester, $schoolYear);
        // Call the updateScoresForStudents method
        try {

            $this->semesterScoreService->updateScoresForStudents($studentTeacherInfo, $semester, $schoolYear);
        } catch (RuntimeException $e) {
            // Handle error
        }
        return redirect()->back()->with('status', 'Failed to update scores.');
    }

    private function isScoresLocked(int $classSectionId, string $semester, string $schoolYear): bool
    {
        return $this->gradingLockModel->isLocked($classSectionId, $semester, $schoolYear);
    }

    private function getStudentsByClassSectionAndYear($classSectionId, $schoolYear)
    {
        // Step 1: Get student IDs from student_class table
        $studentClassRows = $this->studentClassModel
            ->select('student_id')
            ->where('class_section_id', $classSectionId)
            ->where('school_year', $schoolYear)
            ->findAll();

        $studentIds = array_column($studentClassRows, 'student_id');

        if (empty($studentIds)) {
            return []; // No students found
        }

        // Step 2: Get student data from students table
        $students = $this->studentModel
            ->whereIn('id', $studentIds)
            ->where('is_active', 1)
            ->orderBy('lastname', 'ASC')
            ->findAll();

        return $students;
    }

    private function getquizHeaders($classSectionId, $semester, $schoolYear)
    {
        $rows = $this->quizModel
            ->select('quiz_index')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->orderBy('quiz_index', 'ASC')
            ->findAll();

        $headers = [];

        foreach ($rows as $row) {
            $index = $row['quiz_index'];
            if (!is_array($index)) {
                $headers[$index] = true;
            }
        }

        ksort($headers);
        return array_keys($headers);
    }

    private function getClassSectionIdForTeacher($updatedBy)
    {
        $teacherClassModel = new TeacherClassModel();
        $class = $teacherClassModel->where('teacher_id', $updatedBy)->first();
        return $class['class_section_id'] ?? null;
    }


    private function getquizScoresByStudent($classSectionId, $semester, $schoolYear)
    {
        $rows = $this->quizModel
            ->select('student_id, quiz_index, score')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->findAll();

        $studentScores = [];
        foreach ($rows as $row) {
            $studentId = $row['student_id'];
            $index = $row['quiz_index'];
            $score = $row['score'];

            $studentScores[$studentId]['scores'][$index] = $score;
        }

        return $studentScores;
    }

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

        foreach ($studentClasses as $sc) {
            $student = $this->studentModel
                ->where('id', $sc['student_id'])
                ->where('is_active', 1)
                ->first();
            if (!$student) continue;

            $scores = [];
            foreach ($quizHeaders as $index => $ids) {
                $entry = $this->quizModel->where('quiz_index', $ids)
                    ->where('student_id', $sc['student_id'])
                    ->first();
                $scores[$index] = $entry['score'] ?? '';
            }

            $students[] = [
                'student_id' => $sc['student_id'],
                'firstname'  => $student['firstname'],
                'lastname'   => $student['lastname'],
                'scores'     => $scores
            ];
        }

        usort($students, fn($a, $b) => strcmp($a['lastname'], $b['lastname']) ?: strcmp($a['firstname'], $b['firstname']));
        return $students;
    }

    private function getTeacherSelectedSemester(): string
    {
        helper('semester_selection_helper');
        return selected_teacher_semester();
    }
}
