<?php

namespace App\Services\Grading\Validation;

use Illuminate\Support\Facades\DB;

class GradebookFinalizationValidator
{
    /**
     * Strong-mode validation only. Legacy mode deliberately remains displayable/lockable
     * so historical records are not reinterpreted by the new policy.
     */
    public function validateClassSectionBeforeLock(int $classSectionId, string $semester, string $schoolYear): array
    {
        $errors = [];

        foreach ($this->scoreTables() as $table => $meta) {
            if (!DB::getSchemaBuilder()->hasTable($table)) {
                continue;
            }

            $query = DB::table($table)
                ->where('class_section_id', $classSectionId)
                ->where('semester', $semester)
                ->where('school_year', $schoolYear);

            if (DB::getSchemaBuilder()->hasColumn($table, 'status')) {
                $pending = (clone $query)->where(function ($q) {
                    $q->whereNull('status')->orWhere('status', 'pending');
                })->get();

                foreach ($pending as $row) {
                    $errors[] = $this->error($table, $meta['category'], $row, 'pending_score', 'Score must be marked scored, missing, excused, or not_assigned before strong-mode lock.');
                }
            }

            if (DB::getSchemaBuilder()->hasColumn($table, 'max_points')) {
                $invalidMax = (clone $query)->where(function ($q) {
                    $q->whereNull('max_points')->orWhere('max_points', '<=', 0);
                })->get();

                foreach ($invalidMax as $row) {
                    $errors[] = $this->error($table, $meta['category'], $row, 'invalid_max_points', 'Max points must be greater than 0.');
                }

                $invalidScores = (clone $query)
                    ->whereNotNull('score')
                    ->where(function ($q) {
                        $q->where('score', '<', 0)->orWhereRaw('score > max_points');
                    })
                    ->get();

                foreach ($invalidScores as $row) {
                    $errors[] = $this->error($table, $meta['category'], $row, 'score_out_of_range', 'Score must be between 0 and max_points.');
                }
            } else {
                $invalidScores = (clone $query)
                    ->whereNotNull('score')
                    ->where(function ($q) {
                        $q->where('score', '<', 0)->orWhere('score', '>', 100);
                    })
                    ->get();

                foreach ($invalidScores as $row) {
                    $errors[] = $this->error($table, $meta['category'], $row, 'score_out_of_range', 'Score must be between 0 and 100.');
                }
            }
        }

        return [
            'valid' => empty($errors),
            'errors' => $errors,
        ];
    }

    private function scoreTables(): array
    {
        return [
            'homework' => ['category' => 'homework'],
            'quiz' => ['category' => 'quiz'],
            'project' => ['category' => 'project'],
            'participation' => ['category' => 'participation'],
            'midterm_exam' => ['category' => 'midterm_exam'],
            'final_exam' => ['category' => 'final_exam'],
        ];
    }

    private function error(string $table, string $category, object $row, string $code, string $message): array
    {
        return [
            'table' => $table,
            'category' => $category,
            'student_id' => isset($row->student_id) ? (int) $row->student_id : null,
            'item_index' => $row->homework_index ?? $row->quiz_index ?? $row->project_index ?? null,
            'code' => $code,
            'message' => $message,
        ];
    }
}
