<?php

namespace App\Http\Controllers;

use App\Models\Exam;
use App\Models\Student;
use App\Models\Subject;
use App\Models\ResultMark;
use App\Models\FinalResult;
use App\Models\ExamSubjectRule;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;


class ResultManagementController extends Controller
{
    /**
     * Show exam selection page
     */
    public function index(Request $request)
    {
        // If a specific batch is selected
        if ($request->has('batch_id')) {
            $exams = Exam::with('examTemplate')
                ->where('batch_id', $request->batch_id)
                ->latest()
                ->paginate(12);

            $currentBatch = \App\Models\Batch::find($request->batch_id);

            return view('result-management.index', compact('exams', 'currentBatch'));
        }

        // Default Mode: Show Batch Folders
        // We only show batches that have at least one exam, or all active batches
        $batches = \App\Models\Batch::whereHas('exams')
            ->withCount('exams')
            ->orderBy('name', 'desc')
            ->get();

        return view('result-management.batches', compact('batches'));
    }

    /**
     * Show student list for selected exam
     */
    public function selectExam(Exam $exam)
    {
        // Batch scope
        $batchId = $exam->batch_id;

        $batchStudentIds = Student::where('batch_id', $batchId)
            ->whereIn('status', ['active', 'promoted'])
            ->pluck('id');

        // Subjects configured for this exam
        $subjectRules = ExamSubjectRule::with('subject')
            ->where('exam_id', $exam->id)
            ->get();

        $subjectStats = $subjectRules->map(function ($rule) use ($exam, $batchStudentIds) {
            // Count only students who have THIS SPECIFIC SUBJECT assigned
            $studentsWithSubject = Student::whereIn('id', $batchStudentIds)
                ->whereHas('subjects', function ($query) use ($rule) {
                    $query->where('subjects.id', $rule->subject_id);
                })
                ->pluck('id');

            $totalInBatch = $studentsWithSubject->count();

            $completed = ResultMark::where('exam_id', $exam->id)
                ->where('subject_id', $rule->subject_id)
                ->whereIn('student_id', $studentsWithSubject)
                ->distinct('student_id')
                ->count('student_id');
            $pending = max(0, $totalInBatch - $completed);

            return [
                'subject_id' => $rule->subject_id,
                'subject' => $rule->subject,
                'paper_mode' => $rule->paper_mode,
                'total' => $totalInBatch,
                'completed' => $completed,
                'pending' => $pending,
            ];
        });

        return view('result-management.students', compact('exam', 'subjectStats'));
    }

    /**
     * Show mark entry form for a student
     */
    public function enterMarks(Exam $exam, Student $student)
    {
        // Enforce exam -> student batch match
        if ($student->batch_id != $exam->batch_id) {
            return redirect()->route('result-management.students', $exam)
                ->with('error', 'This student does not belong to the exam\'s batch.');
        }
        // Load exam subject rules
        $rules = ExamSubjectRule::with('subject')
            ->where('exam_id', $exam->id)
            ->get();

        if ($rules->isEmpty()) {
            return redirect()->route('result-management.index')
                ->with('error', 'This exam has no subject rules configured!');
        }

        $subjects = $rules->pluck('subject');

        // Get existing marks if any
        $existingMarks = ResultMark::where('exam_id', $exam->id)
            ->where('student_id', $student->id)
            ->get()
            ->keyBy('subject_id');

        // Prepare subjects with rule configuration
        $subjectsWithConfig = $rules->map(function ($rule) use ($existingMarks) {
            $subject = $rule->subject;
            $existingMark = $existingMarks->get($subject->id);

            return [
                'id' => $subject->id,
                'name' => $subject->name,
                'code' => $subject->code,
                'has_first_paper' => $subject->has_first_paper,
                'has_second_paper' => $subject->has_second_paper,
                'has_practical' => $subject->has_practical,
                'paper_mode' => $rule->paper_mode,
                'config' => $rule->config ?? [],
                'total_marks' => $rule->total_marks,
                'pass_marks' => $rule->pass_marks,
                'subject' => $subject,
                'existing_marks' => $existingMark,
            ];
        });

        return view('result-management.enter-marks', compact(
            'exam',
            'student',
            'subjects',
            'subjectsWithConfig',
            'existingMarks'
        ));
    }

    /**
     * Save marks for a student
     */
    public function saveMarks(Request $request, Exam $exam, Student $student)
    {
        // Enforce exam -> student batch match
        if ($student->batch_id != $exam->batch_id) {
            return redirect()->route('result-management.students', $exam)
                ->with('error', 'You can only enter marks for students of this exam\'s batch.');
        }
        $validated = $request->validate([
            'marks' => 'required|array',
            'marks.*.subject_id' => 'required|exists:subjects,id',
            'marks.*.first_paper_mcq' => 'nullable|numeric|min:0',
            'marks.*.first_paper_cq' => 'nullable|numeric|min:0',
            'marks.*.first_paper_practical' => 'nullable|numeric|min:0',
            'marks.*.second_paper_mcq' => 'nullable|numeric|min:0',
            'marks.*.second_paper_cq' => 'nullable|numeric|min:0',
            'marks.*.second_paper_practical' => 'nullable|numeric|min:0',
        ]);

        DB::transaction(function () use ($exam, $student, $validated) {
            // Load exam rules
            $rules = ExamSubjectRule::where('exam_id', $exam->id)
                ->get()
                ->keyBy('subject_id');

            foreach ($validated['marks'] as $mark) {
                $rule = $rules->get($mark['subject_id']);

                if (!$rule) {
                    continue;
                }

                // Calculate total marks
                $totalMarks = ($mark['first_paper_mcq'] ?? 0) +
                    ($mark['first_paper_cq'] ?? 0) +
                    ($mark['first_paper_practical'] ?? 0) +
                    ($mark['second_paper_mcq'] ?? 0) +
                    ($mark['second_paper_cq'] ?? 0) +
                    ($mark['second_paper_practical'] ?? 0);

                // Calculate GPA and Grade
                $ruleTotal = $rule->total_marks > 0 ? $rule->total_marks : 100;
                $percentage = ($totalMarks / $ruleTotal) * 100;

                $gpa = ResultMark::calculateGPAFromPercentage($percentage);
                $grade = ResultMark::getGradeFromPercentage($percentage);
                $isFailed = $totalMarks < $rule->pass_marks;

                ResultMark::updateOrCreate(
                    [
                        'exam_id' => $exam->id,
                        'student_id' => $student->id,
                        'subject_id' => $mark['subject_id'],
                    ],
                    [
                        'first_paper_mcq' => $mark['first_paper_mcq'] ?? null,
                        'first_paper_cq' => $mark['first_paper_cq'] ?? null,
                        'first_paper_practical' => $mark['first_paper_practical'] ?? null,
                        'second_paper_mcq' => $mark['second_paper_mcq'] ?? null,
                        'second_paper_cq' => $mark['second_paper_cq'] ?? null,
                        'second_paper_practical' => $mark['second_paper_practical'] ?? null,
                        'total_marks' => $totalMarks,
                        'subject_gpa' => $gpa,
                        'subject_grade' => $grade,
                        'is_failed' => $isFailed,
                    ]
                );
            }

            // Calculate final result
            $this->calculateFinalResult($exam, $student);
        });

        return redirect()->route('result-management.students', $exam)
            ->with('success', 'Marks saved successfully!');
    }

    /**
     * Subject-wise entry: show all batch students for a subject
     */
    public function subjectEntry(Exam $exam, \App\Models\Subject $subject)
    {
        // Ensure subject is part of exam rules
        $rule = ExamSubjectRule::with('subject')
            ->where('exam_id', $exam->id)
            ->where('subject_id', $subject->id)
            ->first();
        if (!$rule) {
            return redirect()->route('result-management.students', $exam)
                ->with('error', 'This subject is not configured for the selected exam.');
        }

        // Load students of this exam's batch WHO HAVE THIS SUBJECT ASSIGNED
        $students = Student::where('batch_id', $exam->batch_id)
            ->whereIn('status', ['active', 'promoted'])
            ->whereHas('subjects', function ($query) use ($subject) {
                $query->where('subjects.id', $subject->id);
            })
            ->orderBy('class_roll')
            ->paginate(25);

        // Existing marks for this subject across students
        $existing = ResultMark::where('exam_id', $exam->id)
            ->where('subject_id', $subject->id)
            ->get()
            ->keyBy('student_id');

        return view('result-management.subject-entry', compact('exam', 'subject', 'rule', 'students', 'existing'));
    }

    /**
     * Save subject-wise marks for multiple students
     */
    public function saveSubjectMarks(Request $request, Exam $exam, \App\Models\Subject $subject)
    {
        $validated = $request->validate([
            'marks' => 'required|array',
            'marks.*.student_id' => 'required|exists:students,id',
            'marks.*.first_paper_mcq' => 'nullable|numeric|min:0',
            'marks.*.first_paper_cq' => 'nullable|numeric|min:0',
            'marks.*.first_paper_practical' => 'nullable|numeric|min:0',
            'marks.*.second_paper_mcq' => 'nullable|numeric|min:0',
            'marks.*.second_paper_cq' => 'nullable|numeric|min:0',
            'marks.*.second_paper_practical' => 'nullable|numeric|min:0',
        ]);

        // Load exam rules for this subject
        $rule = ExamSubjectRule::where('exam_id', $exam->id)
            ->where('subject_id', $subject->id)
            ->first();
        if (!$rule) {
            return redirect()->back()->with('error', 'Subject rule not found for this exam.');
        }

        DB::transaction(function () use ($validated, $exam, $subject, $rule) {
            foreach ($validated['marks'] as $row) {
                $student = Student::find($row['student_id']);
                if (!$student) {
                    continue;
                }
                // Enforce batch
                if ($student->batch_id != $exam->batch_id) {
                    continue;
                }

                $total = ($row['first_paper_mcq'] ?? 0) + ($row['first_paper_cq'] ?? 0) + ($row['first_paper_practical'] ?? 0)
                    + ($row['second_paper_mcq'] ?? 0) + ($row['second_paper_cq'] ?? 0) + ($row['second_paper_practical'] ?? 0);
                $ruleTotal = $rule->total_marks > 0 ? $rule->total_marks : 100;
                $percentage = ($total / $ruleTotal) * 100;

                $gpa = ResultMark::calculateGPAFromPercentage($percentage);
                $grade = ResultMark::getGradeFromPercentage($percentage);
                $isFailed = $total < ($rule->pass_marks ?? 0);

                ResultMark::updateOrCreate(
                    [
                        'exam_id' => $exam->id,
                        'student_id' => $student->id,
                        'subject_id' => $subject->id,
                    ],
                    [
                        'first_paper_mcq' => $row['first_paper_mcq'] ?? null,
                        'first_paper_cq' => $row['first_paper_cq'] ?? null,
                        'first_paper_practical' => $row['first_paper_practical'] ?? null,
                        'second_paper_mcq' => $row['second_paper_mcq'] ?? null,
                        'second_paper_cq' => $row['second_paper_cq'] ?? null,
                        'second_paper_practical' => $row['second_paper_practical'] ?? null,
                        'total_marks' => $total,
                        'subject_gpa' => $gpa,
                        'subject_grade' => $grade,
                        'is_failed' => $isFailed,
                    ]
                );

                // Recalculate final
                $this->calculateFinalResult($exam, $student);
            }
        });

        return redirect()->route('result-management.subject-entry', ['exam' => $exam, 'subject' => $subject])
            ->with('success', 'Subject-wise marks saved successfully!');
    }

    /**
     * Calculate final result for a student
     */
    private function calculateFinalResult(Exam $exam, Student $student)
    {
        $resultMarks = ResultMark::where('exam_id', $exam->id)
            ->where('student_id', $student->id)
            ->get();

        // Identify the student's 4th/optional subject
        $optionalSubject = $student->subjects()
            ->wherePivot('is_optional', true)
            ->first();
        
        $optionalSubjectId = $optionalSubject ? $optionalSubject->id : null;

        // Separate mandatory and optional subjects
        $mandatoryMarks = $resultMarks->filter(function ($mark) use ($optionalSubjectId) {
            return $mark->subject_id !== $optionalSubjectId;
        });

        $optionalMark = $optionalSubjectId 
            ? $resultMarks->firstWhere('subject_id', $optionalSubjectId) 
            : null;

        // Check if any MANDATORY subject is failed (4th subject doesn't cause overall failure)
        $hasFailure = $mandatoryMarks->contains(function ($resultMark) {
            return $resultMark->is_failed === true;
        });

        // Calculate total marks (sum of all subjects)
        $totalMarks = $resultMarks->sum('total_marks');

        // Calculate GPA with 4th subject special rule
        if ($hasFailure) {
            $gpa = 0.00;
            $grade = 'F';
        } else {
            $mandatoryCount = $mandatoryMarks->count();
            
            if ($mandatoryCount > 0) {
                // Sum of mandatory subject GPAs
                $mandatoryGpaSum = $mandatoryMarks->sum('subject_gpa');
                
                // Add 4th subject bonus: only if GPA > 2.0, add (GPA - 2.0)
                $fourthSubjectBonus = 0;
                if ($optionalMark && $optionalMark->subject_gpa > 2.0) {
                    $fourthSubjectBonus = $optionalMark->subject_gpa - 2.0;
                }
                
                // Final GPA = (Sum of mandatory GPAs + 4th subject bonus) / Count of mandatory subjects
                $gpa = round(($mandatoryGpaSum + $fourthSubjectBonus) / $mandatoryCount, 2);
                
                // Cap GPA at 5.00 (maximum possible)
                $gpa = min($gpa, 5.00);
            } else {
                // Edge case: no mandatory subjects (shouldn't happen in normal scenarios)
                $gpa = round($resultMarks->avg('subject_gpa'), 2);
            }
            
            $grade = $this->getFinalGrade($gpa);
        }

        FinalResult::updateOrCreate(
            [
                'exam_id' => $exam->id,
                'student_id' => $student->id,
            ],
            [
                'total_marks' => $totalMarks,
                'gpa' => $gpa,
                'grade' => $grade,
            ]
        );
    }

    /**
     * Get final grade based on GPA
     */
    private function getFinalGrade($gpa)
    {
        if ($gpa >= 5.00)
            return 'A+';
        if ($gpa >= 4.00)
            return 'A';
        if ($gpa >= 3.50)
            return 'A-';
        if ($gpa >= 3.00)
            return 'B';
        if ($gpa >= 2.00)
            return 'C';
        if ($gpa >= 1.00)
            return 'D';
        return 'F';
    }

    /**
     * View results for an exam
     */
    public function viewResults(Exam $exam)
    {
        // First, calculate positions for all results
        $allResults = FinalResult::where('exam_id', $exam->id)
            ->orderByDesc('gpa')
            ->orderByDesc('total_marks')
            ->get();

        $position = 1;
        $previousGpa = null;
        $previousTotalMarks = null;

        foreach ($allResults as $result) {
            // Handle tied positions (same GPA and total marks)
            if (
                $previousGpa !== null &&
                $result->gpa == $previousGpa &&
                $result->total_marks == $previousTotalMarks
            ) {
                // Same position as previous
                $result->update(['position' => $position - 1]);
            } else {
                // New position
                $result->update(['position' => $position]);
                $previousGpa = $result->gpa;
                $previousTotalMarks = $result->total_marks;
                $position++;
            }
        }

        // Then get paginated results for display
        $results = FinalResult::where('exam_id', $exam->id)
            ->with('student')
            ->orderBy('position')
            ->orderByDesc('gpa')
            ->orderByDesc('total_marks')
            ->paginate(20);

        return view('result-management.view-results', compact('exam', 'results'));
    }

    /**
     * Publish results
     */
    public function publishResults(Exam $exam)
    {
        // First, check if any final results have been calculated
        $finalResultsCount = FinalResult::where('exam_id', $exam->id)->count();

        if ($finalResultsCount === 0) {
            return redirect()->route('result-management.view-results', $exam)
                ->with('error', 'Cannot publish results! No results have been processed yet. Please enter marks for students first.');
        }

        // Get all subjects for this exam
        $subjects = $exam->subjects;

        // Get all students for this exam's batch
        $students = Student::where('ssc_batch', $exam->batch->ssc_batch)
            ->where('hsc_batch', $exam->batch->hsc_batch)
            ->get();

        // Validate that all students have marks for all subjects
        $missingMarks = [];

        foreach ($students as $student) {
            foreach ($subjects as $subject) {
                // Check if student has this subject assigned
                if (!$student->subjects->contains($subject->id)) {
                    continue; // Skip if student doesn't have this subject
                }

                // Check if marks exist for this student-subject combination
                $markExists = ResultMark::where('exam_id', $exam->id)
                    ->where('student_id', $student->id)
                    ->where('subject_id', $subject->id)
                    ->exists();

                if (!$markExists) {
                    $missingMarks[] = [
                        'student' => $student->name . ' (Roll: ' . $student->class_roll . ')',
                        'subject' => $subject->name
                    ];
                }
            }
        }

        // If there are missing marks, prevent publishing
        if (!empty($missingMarks)) {
            $errorMessage = "Cannot publish results! The following students have missing marks:\n\n";

            // Group by student
            $groupedMissing = [];
            foreach ($missingMarks as $item) {
                if (!isset($groupedMissing[$item['student']])) {
                    $groupedMissing[$item['student']] = [];
                }
                $groupedMissing[$item['student']][] = $item['subject'];
            }

            $details = [];
            foreach ($groupedMissing as $studentName => $subjects) {
                $details[] = $studentName . ': ' . implode(', ', $subjects);
            }

            return redirect()->route('result-management.view-results', $exam)
                ->with('error', 'Cannot publish results! ' . count($groupedMissing) . ' student(s) have missing marks. Please enter all marks before publishing.');
        }

        // All validation passed, proceed with publishing
        FinalResult::where('exam_id', $exam->id)
            ->update(['is_published' => true]);

        $exam->update(['status' => 'published']);

        return redirect()->route('result-management.view-results', $exam)
            ->with('success', 'Results published successfully!');
    }

    /**
     * Unpublish results
     */
    /**
     * Unpublish results
     */
    public function unpublishResults(Exam $exam)
    {
        FinalResult::where('exam_id', $exam->id)
            ->update(['is_published' => false]);

        $exam->update(['status' => 'pending']);

        return redirect()->route('result-management.view-results', $exam)
            ->with('success', 'Results unpublished successfully!');
    }

    /**
     * Recalculate Final Results (Background Job)
     */


    /**
     * Export Result to PDF
     */
    public function exportPdf(Exam $exam, Student $student)
    {
        $finalResult = FinalResult::where('exam_id', $exam->id)
            ->where('student_id', $student->id)
            ->firstOrFail();

        $marks = ResultMark::with('subject')
            ->where('exam_id', $exam->id)
            ->where('student_id', $student->id)
            ->get();

        $signature = \App\Models\Setting::get('controller_examination_signature');

        // Return view directly as print layout since PDF library is unavailable
        return view('exports.result-pdf', compact('exam', 'student', 'finalResult', 'marks', 'signature'));
    }

    /**
     * Export Full Result Sheet (Summary & Tabulation)
     */
    public function exportResultSheet(Exam $exam)
    {
        // Fetch all subjects for this exam through ExamSubjectRule
        $subjects = $exam->subjectRules()->with('subject')->get()->pluck('subject');

        // Fetch results with students and their marks eager loaded
        $results = FinalResult::where('exam_id', $exam->id)
            ->with([
                'student' => function ($q) {
                    // Eager load subjects for optional subject detection
                    $q->with('subjects');
                },
                'student.marks' => function ($q) use ($exam) {
                    // Eager load marks for this exam only
                    $q->where('exam_id', $exam->id);
                }
            ])
            ->orderBy('position')
            ->orderByDesc('gpa')
            ->orderByDesc('total_marks')
            ->get();

        $signature = \App\Models\Setting::get('controller_examination_signature');

        return view('exports.result-sheet', compact('exam', 'results', 'subjects', 'signature'));
    }
}
