<?php

namespace App\Services\Scores;

use App\Models\GradingLock;
use App\Models\ClassSection;
use App\Services\Grading\Validation\ScoreValueValidator;
use App\Models\MissingScoreOverride;
use App\Models\Participation;
use App\Models\Student;
use App\Models\StudentClass;
use App\Models\TeacherClass;

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

    public function resolveClassSectionIdForTeacher(int $teacherId, ?int $requestedClassSectionId, ?string $schoolYearInput): ?int
    {
        if ($teacherId <= 0) {
            return null;
        }

        $schoolYear = $this->term->schoolYear($schoolYearInput);
        $query = TeacherClass::query()->where('teacher_id', $teacherId);
        if ($schoolYear !== '') {
            $query->where('school_year', $schoolYear);
        }

        $allowedIds = $query->pluck('class_section_id')
            ->map(fn ($v) => (int) $v)
            ->filter(fn ($v) => $v > 0)
            ->unique()
            ->values()
            ->all();

        if ($allowedIds === []) {
            return null;
        }

        if ($requestedClassSectionId !== null && $requestedClassSectionId > 0 && in_array($requestedClassSectionId, $allowedIds, true)) {
            return $requestedClassSectionId;
        }

        return $allowedIds[0];
    }

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

        $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,
                'school_id' => $row->school_id,
                'firstname' => (string) $row->firstname,
                'lastname' => (string) $row->lastname,
                'scores' => [],
            ])
            ->all();

        $scoresRows = Participation::query()
            ->select('student_id', 'score')
            ->where('class_section_id', $classSectionId)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->get();

        $scoreMap = [];
        foreach ($scoresRows as $row) {
            $score = $row->score;
            $scoreMap[(int) $row->student_id] = is_numeric($score) ? (float) $score : $score;
        }

        foreach ($students as &$student) {
            $score = $scoreMap[$student['student_id']] ?? '';
            $student['scores'] = [
                'score' => $score,
            ];
        }
        unset($student);

        return [
            'students' => $students,
            'classSectionName' => (string) (ClassSection::query()->where('class_section_id', $classSectionId)->value('class_section_name') ?? ''),
            'semester' => $semester,
            'schoolYear' => $schoolYear,
            'scoresLocked' => GradingLock::isLocked($classSectionId, $semester, $schoolYear),
            'missingOkMap' => MissingScoreOverride::getOverridesMap($classSectionId, $semester, $schoolYear, 'participation'),
        ];
    }

    public function listBySchoolYear(?string $schoolYear): array
    {
        $schoolYear = $this->term->schoolYear($schoolYear);
        $schoolYearVariants = $this->term->schoolYearVariants($schoolYear);
        if (empty($schoolYearVariants)) {
            $schoolYearVariants = [$schoolYear];
        }

        $rows = Participation::query()
            ->select([
                'participation.id',
                'participation.student_id',
                'participation.school_id',
                'participation.class_section_id',
                'participation.updated_by',
                'participation.score',
                'participation.comment',
                'participation.semester',
                'participation.school_year',
                'participation.created_at',
                'participation.updated_at',
                's.firstname as student_firstname',
                's.lastname as student_lastname',
            ])
            ->leftJoin('students as s', 's.id', '=', 'participation.student_id')
            ->whereIn('participation.school_year', $schoolYearVariants)
            ->orderBy('participation.class_section_id')
            ->orderBy('s.lastname')
            ->orderBy('s.firstname')
            ->get()
            ->map(fn ($row) => $row->toArray())
            ->all();

        return [
            'rows' => $rows,
            'schoolYear' => $schoolYear,
        ];
    }

    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);
        $checkedItems = [];
        foreach ($missingOk as $studentId => $flags) {
            $checkedItems[] = [
                'student_id' => (int) $studentId,
                'item_index' => null,
            ];
        }

        MissingScoreOverride::replaceOverrides(
            $classSectionId,
            $semester,
            $schoolYear,
            'participation',
            $studentIds,
            null,
            $checkedItems,
            $updatedBy,
            true
        );

        $count = 0;
        foreach ($scores as $studentId => $data) {
            if (!is_numeric($studentId)) {
                continue;
            }
            if (!is_array($data)) {
                $data = ['score' => $data];
            }

            $student = Student::query()->find((int) $studentId);
            if (!$student) {
                continue;
            }

            $rawScore = $data['score'] ?? null;
            $validator = new ScoreValueValidator();
            $normalizedScore = $validator->normalizeNullable($rawScore, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Participation score');
            $missingAllowed = !empty($missingOk[$studentId]);

            $existing = Participation::query()
                ->where('student_id', $studentId)
                ->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,
                'score' => $normalizedScore,
                'max_points' => ScoreValueValidator::DEFAULT_MAX_POINTS,
                'status' => $validator->inferStatus($normalizedScore, $missingAllowed),
                'semester' => $semester,
                'school_year' => $schoolYear,
                'updated_at' => now(),
            ];

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

        return $count;
    }
}
