<?php

namespace App\Services\Scores;

use App\Models\GradingLock;
use App\Models\MissingScoreOverride;
use App\Models\Quiz;
use App\Models\Student;
use App\Models\StudentClass;

class QuizScoreService
{
    public function __construct(private ScoreTermService $term)
    {
    }

    public function list(int $classSectionId, ?string $semester, ?string $schoolYear): array
    {
        $semester = $this->term->semesterLabel($semester);
        $schoolYear = $this->term->schoolYear($schoolYear);

        $headerRows = Quiz::query()
            ->select('quiz_index')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->orderBy('quiz_index')
            ->get();

        $headers = $headerRows->pluck('quiz_index')->map(fn ($v) => (int) $v)->unique()->values()->all();

        $studentIds = StudentClass::query()
            ->where('class_section_id', $classSectionId)
            ->where('school_year', $schoolYear)
            ->pluck('student_id')
            ->map(fn ($v) => (int) $v)
            ->all();

        $students = Student::query()
            ->whereIn('id', $studentIds)
            ->where('is_active', 1)
            ->orderBy('lastname')
            ->orderBy('firstname')
            ->get()
            ->map(fn ($row) => [
                'student_id' => (int) $row->id,
                'firstname' => (string) $row->firstname,
                'lastname' => (string) $row->lastname,
                'scores' => [],
            ])
            ->all();

        $scoresRows = Quiz::query()
            ->select('student_id', 'quiz_index', 'score')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->get()
            ->map(fn ($row) => $row->toArray())
            ->all();

        $scoresMap = [];
        foreach ($scoresRows as $row) {
            $score = $row['score'] ?? null;
            $scoresMap[(int) $row['student_id']][(int) $row['quiz_index']] = is_numeric($score) ? (float) $score : $score;
        }

        foreach ($students as &$student) {
            foreach ($headers as $index) {
                $student['scores'][(int) $index] = $scoresMap[$student['student_id']][$index] ?? '';
            }
        }
        unset($student);

        return [
            'students' => $students,
            'headers' => $headers,
            'semester' => $semester,
            'schoolYear' => $schoolYear,
            'scoresLocked' => GradingLock::isLocked($classSectionId, $semester, $schoolYear),
            'missingOkMap' => MissingScoreOverride::getOverridesMap($classSectionId, $semester, $schoolYear, 'quiz'),
        ];
    }

    public function update(int $classSectionId, string $semester, string $schoolYear, array $scores, array $missingOk, int $updatedBy): int
    {
        if (GradingLock::isLocked($classSectionId, $semester, $schoolYear)) {
            throw new \RuntimeException('Scores are locked for this class.');
        }

        $studentIds = array_keys($scores);
        $indexSet = [];
        foreach ($scores as $quizData) {
            if (!is_array($quizData)) {
                continue;
            }
            foreach ($quizData as $index => $_) {
                if (is_numeric($index)) {
                    $indexSet[(int) $index] = true;
                }
            }
        }

        $checkedItems = [];
        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,
                ];
            }
        }

        MissingScoreOverride::replaceOverrides(
            $classSectionId,
            $semester,
            $schoolYear,
            'quiz',
            $studentIds,
            array_keys($indexSet),
            $checkedItems,
            $updatedBy
        );

        $count = 0;
        foreach ($scores as $studentId => $quizData) {
            if (!is_numeric($studentId)) {
                continue;
            }
            if (!is_array($quizData)) {
                $quizData = [1 => $quizData];
            }
            $student = Student::query()->find((int) $studentId);
            if (!$student) {
                continue;
            }
            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 = Quiz::query()
                    ->where('student_id', $studentId)
                    ->where('quiz_index', $quizNumber)
                    ->where('class_section_id', $classSectionId)
                    ->where('semester', $semester)
                    ->where('school_year', $schoolYear)
                    ->first();

                $payload = [
                    'student_id' => (int) $studentId,
                    'school_id' => $student->school_id,
                    'class_section_id' => $classSectionId,
                    'updated_by' => $updatedBy,
                    'quiz_index' => (int) $quizNumber,
                    'score' => $normalizedScore,
                    'semester' => $semester,
                    'school_year' => $schoolYear,
                    'updated_at' => now(),
                ];

                if ($existing) {
                    $existing->update($payload);
                } else {
                    $payload['created_at'] = now();
                    Quiz::query()->create($payload);
                }
                $count++;
            }
        }

        return $count;
    }

    public function addColumn(int $classSectionId, string $semester, string $schoolYear, int $updatedBy): int
    {
        $maxIndex = (int) Quiz::query()
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->max('quiz_index');

        $nextIndex = $maxIndex + 1;

        $students = StudentClass::query()
            ->where('class_section_id', $classSectionId)
            ->where('school_year', $schoolYear)
            ->get();

        foreach ($students as $student) {
            $studentRow = Student::query()->find((int) $student->student_id);
            if (!$studentRow) {
                continue;
            }
            $exists = Quiz::query()
                ->where('student_id', $student->student_id)
                ->where('quiz_index', $nextIndex)
                ->where('class_section_id', $classSectionId)
                ->where('semester', $semester)
                ->where('school_year', $schoolYear)
                ->first();

            if ($exists) {
                continue;
            }

            Quiz::query()->create([
                'student_id' => (int) $student->student_id,
                'school_id' => $studentRow->school_id,
                'class_section_id' => $classSectionId,
                'updated_by' => $updatedBy,
                'quiz_index' => $nextIndex,
                'score' => null,
                'semester' => $semester,
                'school_year' => $schoolYear,
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        }

        return $nextIndex;
    }
}
