<?php

namespace App\Services\Grading;

use App\Models\FinalExam;
use App\Models\GradingLock;
use App\Models\Homework;
use App\Models\MidtermExam;
use App\Models\Project;
use App\Models\Quiz;
use App\Models\ScoreComment;
use App\Models\SemesterScore;
use App\Models\Student;
use App\Services\Scores\SemesterScoreService;
use RuntimeException;

class GradingScoreService
{
    public function __construct(private SemesterScoreService $semesterScoreService)
    {
    }

    public function show(string $type, int $classSectionId, int $studentId, string $semester, string $schoolYear): array
    {
        $model = $this->resolveModel($type);
        $student = Student::query()->find($studentId);

        if (!$student) {
            throw new RuntimeException('Student not found.');
        }

        $scores = $model->newQuery()
            ->where('student_id', $studentId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->get();

        $scoresLocked = $classSectionId > 0
            ? GradingLock::isLocked($classSectionId, $semester, $schoolYear)
            : false;

        return [
            'student' => $student,
            'scores' => $scores,
            'type' => $type,
            'class_section_id' => $classSectionId,
            'semester' => $semester,
            'scores_locked' => $scoresLocked,
        ];
    }

    public function update(array $payload, int $updatedBy): void
    {
        $type = (string) ($payload['type'] ?? '');
        $studentId = (int) ($payload['student_id'] ?? 0);
        $classSectionId = (int) ($payload['class_section_id'] ?? 0);
        $semester = (string) ($payload['semester'] ?? '');
        $schoolYear = (string) ($payload['school_year'] ?? '');

        if ($classSectionId > 0 && GradingLock::isLocked($classSectionId, $semester, $schoolYear)) {
            throw new RuntimeException('Scores are locked for this class. Unlock to edit.');
        }

        $model = $this->resolveModel($type);

        if (in_array($type, ['homework', 'quiz', 'project'], true)) {
            $scoreIds = $payload['score_ids'] ?? [];
            $scores = $payload['scores'] ?? [];
            $comments = $payload['comments'] ?? [];

            foreach ($scoreIds as $i => $id) {
                $model->newQuery()->whereKey($id)->update([
                    'score' => $scores[$i] ?? null,
                    'comment' => $comments[$i] ?? null,
                    'updated_at' => now(),
                ]);
            }
        } elseif (in_array($type, ['midterm', 'final', 'test'], true)) {
            $score = $payload['score'] ?? null;

            $data = [
                'score' => $score,
                'updated_at' => now(),
            ];

            $existing = $model->newQuery()
                ->where('student_id', $studentId)
                ->where('class_section_id', $classSectionId)
                ->where('semester', $semester)
                ->where('school_year', $schoolYear)
                ->first();

            if ($existing) {
                $existing->update($data);
            } else {
                $model->newQuery()->create($data + [
                    'student_id' => $studentId,
                    'class_section_id' => $classSectionId,
                    'semester' => $semester,
                    'school_year' => $schoolYear,
                    'created_at' => now(),
                ]);
            }
        } elseif ($type === 'comments') {
            $comment = (string) ($payload['comment'] ?? '');

            $model->newQuery()
                ->where('student_id', $studentId)
                ->where('semester', $semester)
                ->where('school_year', $schoolYear)
                ->delete();

            $model->newQuery()->create([
                'student_id' => $studentId,
                'score_type' => 'general',
                'semester' => $semester,
                'school_year' => $schoolYear,
                'comment' => $comment,
                'commented_by' => $updatedBy ?: null,
                'created_at' => now(),
            ]);
        }

        $studentTeacherInfo = Student::getStudentInfoByClassSectionId($classSectionId, $semester, $schoolYear, $updatedBy);
        if (!empty($studentTeacherInfo)) {
            try {
                $this->semesterScoreService->updateScoresForStudents($studentTeacherInfo, $semester, $schoolYear);
            } catch (RuntimeException $e) {
            }
        }
    }

    public function getScoreComments(string $semester, string $schoolYear): array
    {
        $studentIds = Student::query()
            ->select('students.id')
            ->join('student_class', 'student_class.student_id', '=', 'students.id')
            ->where('student_class.semester', $semester)
            ->where('student_class.school_year', $schoolYear)
            ->pluck('students.id')
            ->map(fn ($v) => (int) $v)
            ->all();

        if (empty($studentIds)) {
            return [];
        }

        $scores = [];
        $students = Student::query()
            ->whereIn('id', $studentIds)
            ->where('school_year', $schoolYear)
            ->get();

        foreach ($students as $student) {
            $scores[$student->id] = [
                'school_id' => $student->school_id,
                'firstname' => $student->firstname,
                'lastname' => $student->lastname,
                'comments' => [],
            ];
        }

        $commentRows = ScoreComment::query()
            ->whereIn('student_id', $studentIds)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->get();

        foreach ($commentRows as $comment) {
            $scores[$comment->student_id]['comments'][] = $comment;
        }

        return $scores;
    }

    private function resolveModel(string $type)
    {
        return match ($type) {
            'homework' => new Homework(),
            'quiz' => new Quiz(),
            'project' => new Project(),
            'midterm' => new MidtermExam(),
            'final' => new FinalExam(),
            'test' => new SemesterScore(),
            'comments' => new ScoreComment(),
            default => throw new RuntimeException("Invalid type: {$type}"),
        };
    }
}
