<?php

namespace App\Services;

use App\Models\Exam;
use App\Models\ExamPaper;
use App\Models\GradeScale;
use App\Models\Mark;
use App\Models\Student;
use Illuminate\Support\Collection;

/**
 * Computes results from raw marks: per-subject grade/GPA, aggregate percentage,
 * overall grade/GPA, pass/fail, and class positions.
 */
class ResultService
{
    /** Full result for one student in an exam. */
    public function studentResult(Exam $exam, Student $student): array
    {
        $papers = ExamPaper::where('exam_id', $exam->id)->where('class_id', $student->class_id)
            ->with('subject')->get();

        $marks = Mark::where('exam_id', $exam->id)->where('student_id', $student->id)
            ->get()->keyBy('subject_id');

        $rows = collect();
        $totalObtained = 0.0;
        $totalMax = 0.0;
        $gpaSum = 0.0;
        $failed = false;

        foreach ($papers as $paper) {
            $mark = $marks->get($paper->subject_id);
            $absent = $mark?->is_absent ?? false;
            $obtained = $absent ? 0 : (float) ($mark->obtained_marks ?? 0);
            $max = (float) $paper->total_marks;
            $pct = $max > 0 ? round($obtained / $max * 100, 2) : 0;
            $band = GradeScale::forPercentage($exam->branch_id, $pct);
            $pass = ! $absent && $obtained >= (float) $paper->passing_marks;
            if (! $pass) {
                $failed = true;
            }

            $totalObtained += $obtained;
            $totalMax += $max;
            $gpaSum += (float) ($band->gpa ?? 0);

            $rows->push([
                'subject' => $paper->subject?->name,
                'total' => $max,
                'passing' => (float) $paper->passing_marks,
                'obtained' => $obtained,
                'absent' => $absent,
                'percentage' => $pct,
                'grade' => $band->grade ?? '-',
                'gpa' => (float) ($band->gpa ?? 0),
                'pass' => $pass,
            ]);
        }

        $count = $papers->count();
        $overallPct = $totalMax > 0 ? round($totalObtained / $totalMax * 100, 2) : 0;
        $overallBand = GradeScale::forPercentage($exam->branch_id, $overallPct);

        return [
            'rows' => $rows,
            'total_obtained' => $totalObtained,
            'total_max' => $totalMax,
            'percentage' => $overallPct,
            'grade' => $overallBand->grade ?? '-',
            'gpa' => $count > 0 ? round($gpaSum / $count, 2) : 0,
            'status' => $failed ? 'Fail' : 'Pass',
            'has_marks' => $count > 0 && $marks->isNotEmpty(),
        ];
    }

    /** Aggregate per student for a class, ranked by total obtained (position). */
    public function classResults(Exam $exam, int $classId): Collection
    {
        $students = Student::where('class_id', $classId)->where('status', 'active')->orderBy('name')->get();

        $results = $students->map(function ($student) use ($exam) {
            $r = $this->studentResult($exam, $student);

            return [
                'student' => $student,
                'total_obtained' => $r['total_obtained'],
                'total_max' => $r['total_max'],
                'percentage' => $r['percentage'],
                'grade' => $r['grade'],
                'gpa' => $r['gpa'],
                'status' => $r['status'],
                'has_marks' => $r['has_marks'],
            ];
        })->filter(fn ($r) => $r['has_marks'])->values();

        // Assign positions (dense ranking by total obtained, descending).
        $sorted = $results->sortByDesc('total_obtained')->values();
        $position = 0;
        $prev = null;
        $rankMap = [];
        foreach ($sorted as $i => $row) {
            if ($prev === null || $row['total_obtained'] < $prev) {
                $position = $i + 1;
            }
            $rankMap[$row['student']->id] = $position;
            $prev = $row['total_obtained'];
        }

        return $results->map(function ($r) use ($rankMap) {
            $r['position'] = $rankMap[$r['student']->id] ?? null;

            return $r;
        });
    }

    public function positionHolders(Exam $exam, int $classId, int $limit = 3): Collection
    {
        return $this->classResults($exam, $classId)
            ->sortBy('position')->take($limit)->values();
    }

    /** Per-subject statistics for a class in an exam. */
    public function subjectAnalysis(Exam $exam, int $classId): Collection
    {
        $papers = ExamPaper::where('exam_id', $exam->id)->where('class_id', $classId)->with('subject')->get();

        return $papers->map(function ($paper) use ($exam) {
            $marks = Mark::where('exam_id', $exam->id)->where('subject_id', $paper->subject_id)
                ->whereHas('student', fn ($q) => $q->where('class_id', $paper->class_id))
                ->where('is_absent', false)->pluck('obtained_marks')->map(fn ($m) => (float) $m);

            $count = $marks->count();
            $passing = (float) $paper->passing_marks;

            return [
                'subject' => $paper->subject?->name,
                'total' => (float) $paper->total_marks,
                'appeared' => $count,
                'highest' => $count ? $marks->max() : 0,
                'lowest' => $count ? $marks->min() : 0,
                'average' => $count ? round($marks->avg(), 2) : 0,
                'passed' => $marks->filter(fn ($m) => $m >= $passing)->count(),
                'pass_pct' => $count ? round($marks->filter(fn ($m) => $m >= $passing)->count() / $count * 100, 1) : 0,
            ];
        });
    }
}
