<?php

namespace Modules\Results\Services;

use Modules\Results\Models\Result;
use Modules\Results\Models\StudentResultSummary;
use Modules\Students\Models\Student;
use Modules\Academic\Models\AcademicYear;
use Modules\Academic\Models\ExamType;
use Modules\Configuration\Models\DivisionRule;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class ResultCalculationService
{
    /**
     * Calculate and store result summary for a student
     */
    public function calculateStudentSummary(int $studentId, int $academicYearId, int $examTypeId): StudentResultSummary
    {
        $student = Student::findOrFail($studentId);
        $enrollment = $student->getCurrentEnrollment($academicYearId);

        if (!$enrollment) {
            throw new \Exception("Student is not enrolled in the specified academic year");
        }

        $results = Result::forStudent($studentId)
            ->forAcademicYear($academicYearId)
            ->forExamType($examTypeId)
            ->with(['subject'])
            ->get();

        if ($results->isEmpty()) {
            throw new \Exception("No results found for the student");
        }

        // Calculate totals and averages from ALL subjects
        $totalMarks = $results->sum('marks');
        $subjectsCount = $results->count();
        $averageMarks = round($totalMarks / $subjectsCount, 2);

        // Calculate total points from top 7 subjects only (lowest points = best performance)
        $topSubjectsPoints = $results->sortBy('points')
            ->take(7)
            ->sum('points');

        // Determine division based on total points
        $division = DivisionRule::getDivisionForPoints($topSubjectsPoints, $academicYearId);

        // Set rank to 0 initially - will be calculated later in batch
        $rank = 0;

        // Create or update summary
        $summary = StudentResultSummary::updateOrCreate(
            [
                'student_id' => $studentId,
                'academic_year_id' => $academicYearId,
                'exam_type_id' => $examTypeId,
            ],
            [
                'classroom_id' => $enrollment->classroom_id,
                'total_marks' => $totalMarks,
                'average_marks' => $averageMarks,
                'total_points' => $topSubjectsPoints,
                'division' => $division,
                'rank' => $rank,
                'subjects_count' => $subjectsCount,
                'computed_at' => now(),
            ]
        );

        return $summary;
    }

    /**
     * Calculate rank for a student within their classroom
     */
    private function calculateRank(int $studentId, int $academicYearId, int $examTypeId, int $classroomId, int $totalMarks): int
    {
        $higherScores = StudentResultSummary::forAcademicYear($academicYearId)
            ->forExamType($examTypeId)
            ->forClassroom($classroomId)
            ->where('total_marks', '>', $totalMarks)
            ->count();

        return $higherScores + 1;
    }

    /**
     * Recalculate all ranks for a classroom after new results are added
     */
    public function recalculateClassroomRanks(int $classroomId, int $academicYearId, int $examTypeId): void
    {
        $summaries = StudentResultSummary::forAcademicYear($academicYearId)
            ->forExamType($examTypeId)
            ->forClassroom($classroomId)
            ->orderBy('total_marks', 'desc')
            ->get();

        $currentRank = 1;
        $previousMarks = null;
        $position = 1;

        foreach ($summaries as $summary) {
            // If marks are different from previous student, update the rank
            if ($previousMarks !== null && $summary->total_marks < $previousMarks) {
                $currentRank = $position;
            }

            $summary->update(['rank' => $currentRank]);
            $previousMarks = $summary->total_marks;
            $position++;
        }
    }

    /**
     * Calculate summaries for all students in a classroom
     */
    public function calculateClassroomSummaries(int $classroomId, int $academicYearId, int $examTypeId): Collection
    {
        $enrollments = \Modules\Enrollment\Models\Enrollment::forClassroom($classroomId)
            ->forAcademicYear($academicYearId)
            ->active()
            ->with('student')
            ->get();

        $summaries = collect();

        foreach ($enrollments as $enrollment) {
            try {
                $summary = $this->calculateStudentSummary(
                    $enrollment->student_id,
                    $academicYearId,
                    $examTypeId
                );
                $summaries->push($summary);
            } catch (\Exception $e) {
                // Log the error but continue with other students
                \Log::warning("Failed to calculate summary for student {$enrollment->student_id}: " . $e->getMessage());
            }
        }

        // Recalculate ranks after all summaries are computed
        $this->recalculateClassroomRanks($classroomId, $academicYearId, $examTypeId);

        return $summaries;
    }

    /**
     * Get comprehensive result data for a student
     */
    public function getStudentResultData(int $studentId, int $academicYearId, int $examTypeId): array
    {
        $student = Student::with(['user', 'enrollments.classroom'])->findOrFail($studentId);

        $results = Result::forStudent($studentId)
            ->forAcademicYear($academicYearId)
            ->forExamType($examTypeId)
            ->with(['subject', 'examType', 'academicYear'])
            ->get();

        $summary = StudentResultSummary::forStudent($studentId)
            ->forAcademicYear($academicYearId)
            ->forExamType($examTypeId)
            ->first();

        return [
            'student' => $student,
            'results' => $results,
            'summary' => $summary,
            'subjects_breakdown' => $this->getSubjectsBreakdown($results),
            'performance_metrics' => $this->getPerformanceMetrics($results, $summary),
        ];
    }

    /**
     * Get subjects breakdown with grades and points
     */
    private function getSubjectsBreakdown(Collection $results): array
    {
        return $results->map(function ($result) {
            return [
                'subject' => $result->subject->name,
                'subject_code' => $result->subject->code,
                'marks' => $result->marks,
                'grade' => $result->grade,
                'points' => $result->points,
            ];
        })->toArray();
    }

    /**
     * Get performance metrics
     */
    private function getPerformanceMetrics(Collection $results, ?StudentResultSummary $summary): array
    {
        if (!$summary) {
            return [];
        }

        $gradeDistribution = $results->groupBy('grade')->map->count();
        $highestMarks = $results->max('marks');
        $lowestMarks = $results->min('marks');

        return [
            'grade_distribution' => $gradeDistribution,
            'highest_marks' => $highestMarks,
            'lowest_marks' => $lowestMarks,
            'total_marks' => $summary->total_marks,
            'average_marks' => $summary->average_marks,
            'total_points' => $summary->total_points,
            'division' => $summary->division,
            'rank' => $summary->rank,
            'subjects_count' => $summary->subjects_count,
        ];
    }

    /**
     * Bulk calculate summaries for multiple students
     */
    public function bulkCalculateSummaries(array $studentIds, int $academicYearId, int $examTypeId): array
    {
        $results = [];

        DB::transaction(function () use ($studentIds, $academicYearId, $examTypeId, &$results) {
            foreach ($studentIds as $studentId) {
                try {
                    $summary = $this->calculateStudentSummary($studentId, $academicYearId, $examTypeId);
                    $results[] = [
                        'student_id' => $studentId,
                        'status' => 'success',
                        'summary' => $summary,
                    ];
                } catch (\Exception $e) {
                    $results[] = [
                        'student_id' => $studentId,
                        'status' => 'error',
                        'message' => $e->getMessage(),
                    ];
                }
            }
        });

        return $results;
    }

    /**
     * Get consolidated results grouped by student and exam type
     */
    public function getConsolidatedResults($filters = []): array
    {
        $query = Result::with([
            'student.user',
            'subject',
            'examType',
            'academicYear',
            'classroom'
        ]);

        // Apply filters
        if (!empty($filters['academic_year_id'])) {
            $query->forAcademicYear($filters['academic_year_id']);
        }
        if (!empty($filters['exam_type_id'])) {
            $query->forExamType($filters['exam_type_id']);
        }
        if (!empty($filters['classroom_id'])) {
            $query->forClassroom($filters['classroom_id']);
        }
        if (!empty($filters['subject_id'])) {
            $query->forSubject($filters['subject_id']);
        }

        $results = $query->get();

        // Group results by student and exam type
        $groupedResults = $results->groupBy(function ($result) {
            return $result->student_id . '_' . $result->exam_type_id . '_' . $result->academic_year_id;
        });

        $consolidatedResults = [];

        foreach ($groupedResults as $key => $studentExamResults) {
            $firstResult = $studentExamResults->first();

            // Get student summary for this exam type
            $summary = StudentResultSummary::forStudent($firstResult->student_id)
                ->forAcademicYear($firstResult->academic_year_id)
                ->forExamType($firstResult->exam_type_id)
                ->first();

            // Organize subjects data
            $subjectsData = [];
            foreach ($studentExamResults as $result) {
                $subjectsData[$result->subject->id] = [
                    'subject' => $result->subject,
                    'marks' => $result->marks,
                    'grade' => $result->grade,
                    'points' => $result->points,
                ];
            }

            // If summary doesn't exist, calculate it on-the-fly
            if (!$summary) {
                try {
                    $summary = $this->calculateStudentSummary(
                        $firstResult->student_id,
                        $firstResult->academic_year_id,
                        $firstResult->exam_type_id
                    );
                } catch (\Exception $e) {
                    // If calculation fails, create a basic summary from current results
                    $totalMarks = $studentExamResults->sum('marks');
                    $totalPoints = $studentExamResults->sum('points');
                    $averageMarks = $studentExamResults->count() > 0 ? round($totalMarks / $studentExamResults->count(), 2) : 0;

                    $summary = (object) [
                        'total_marks' => $totalMarks,
                        'average_marks' => $averageMarks,
                        'total_points' => $totalPoints,
                        'division' => 'N/A',
                        'rank' => 'N/A',
                        'subjects_count' => $studentExamResults->count(),
                    ];
                }
            }

            $consolidatedResults[] = [
                'id' => $key, // Unique identifier for this row
                'student' => $firstResult->student,
                'exam_type' => $firstResult->examType,
                'academic_year' => $firstResult->academicYear,
                'classroom' => $firstResult->classroom,
                'subjects_data' => $subjectsData,
                'summary' => $summary,
                'total_marks' => $summary->total_marks ?? 0,
                'average_marks' => $summary->average_marks ?? 0,
                'total_points' => $summary->total_points ?? 0,
                'division' => $summary->division ?? 'N/A',
                'rank' => $summary->rank ?? 'N/A',
                'subjects_count' => count($subjectsData),
            ];
        }

        return $consolidatedResults;
    }
}
