File "ExamController.php"

Full Path: /home/trinadezambia/public_html/admin_panel/app/Http/Controllers/Exam/ExamController.php
File size: 62.68 KB
MIME-type: text/x-php
Charset: utf-8

<?php

namespace App\Http\Controllers\Exam;

use App\Exports\MarksDataExport;
use App\Imports\MarksDataImport;
use App\Http\Controllers\Controller;
use App\Models\ExamResult;
use App\Models\ExamTimetable;
use App\Models\ClassSection;
use App\Repositories\ClassSchool\ClassSchoolInterface;
use App\Repositories\ClassSection\ClassSectionInterface;
use App\Repositories\ClassSubject\ClassSubjectInterface;
use App\Repositories\ClassTeachers\ClassTeachersInterface;
use App\Repositories\Exam\ExamInterface;
use App\Repositories\ExamMarks\ExamMarksInterface;
use App\Repositories\ExamResult\ExamResultInterface;
use App\Repositories\ExamTimetable\ExamTimetableInterface;
use App\Repositories\Grades\GradesInterface;
use App\Repositories\Medium\MediumInterface;
use App\Repositories\SessionYear\SessionYearInterface;
use App\Repositories\Student\StudentInterface;
use App\Repositories\StudentSubject\StudentSubjectInterface;
use App\Repositories\Subject\SubjectInterface;
use App\Repositories\User\UserInterface;
use App\Services\BootstrapTableService;
use App\Services\CachingService;
use App\Services\ResponseService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use PDF;
use Throwable;
use Excel;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Validator;
use App\Models\ClassTeacher;
use Illuminate\Support\Str;

use function PHPUnit\Framework\isEmpty;

class ExamController extends Controller
{
    private ExamInterface $exam;
    private ClassSchoolInterface $class;
    private SessionYearInterface $sessionYear;
    private SubjectInterface $subject;
    private ExamTimetableInterface $examTimetable;
    private ClassSectionInterface $classSection;
    private ExamMarksInterface $examMarks;
    private ExamResultInterface $examResult;
    private StudentSubjectInterface $studentSubject;
    private ClassSubjectInterface $classSubject;
    private UserInterface $users;
    private CachingService $cache;
    private MediumInterface $mediums;
    private ClassTeachersInterface $classTeacher;
    private GradesInterface $grade;
    private StudentInterface $student;

    public function __construct(ExamInterface $exam, ClassSchoolInterface $class, SessionYearInterface $sessionYear, SubjectInterface $subject, ExamTimetableInterface $examTimetable, ClassSectionInterface $classSection, ExamMarksInterface $examMarks, ExamResultInterface $examResult, StudentSubjectInterface $studentSubject, ClassSubjectInterface $classSubject, UserInterface $users, CachingService $cache, MediumInterface $mediums, ClassTeachersInterface $classTeacher, GradesInterface $grade, StudentInterface $student)
    {
        $this->exam = $exam;
        $this->class = $class;
        $this->sessionYear = $sessionYear;
        $this->subject = $subject;
        $this->examTimetable = $examTimetable;
        $this->classSection = $classSection;
        $this->examMarks = $examMarks;
        $this->examResult = $examResult;
        $this->studentSubject = $studentSubject;
        $this->classSubject = $classSubject;
        $this->users = $users;
        $this->cache = $cache;
        $this->mediums = $mediums;
        $this->classTeacher = $classTeacher;
        $this->grade = $grade;
        $this->student = $student;
    }

    public function index()
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenRedirect('exam-create');
        $classes = $this->class->all(['*'], ['stream', 'medium', 'shift']);
        $subjects = $this->subject->builder()->orderBy('id', 'DESC')->get();
        $mediums = $this->mediums->builder()->pluck('name', 'id');
        return response(view('exams.index', compact('classes', 'subjects', 'mediums')));
    }

    public function store(Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenSendJson('exam-create');
        $request->validate([
            'name' => 'required',
            'class_id' => 'required|array',
            'class_id.*' => 'exists:classes,id'
        ]);

        try {
            DB::beginTransaction();
            $sessionYear = $this->cache->getSessionYear();
            $sessionYearId = $sessionYear->id;
            $examData = [];

            // Loop through each class ID and create exam records
            foreach ($request->class_id as $classId) {
                $exam = $this->exam->create([
                    'name' => $request->name,
                    'session_year_id' => $sessionYearId,
                    'description' => $request->description,
                    'start_date' => $request->start_date,
                    'end_date' => $request->end_date,
                    'school_id' => Auth::user()->school_id,
                    'publish' => $request->publish ?? 0,
                    'last_result_submission_date' => $request->last_result_submission_date,
                    'class_id' => $classId
                ]);

                $examData[] = $exam;
            }

            DB::commit();
            ResponseService::successResponse('Data Stored Successfully');
        } catch (Throwable $e) {
            if (
                Str::contains($e->getMessage(), [
                    'does not exist',
                    'file_get_contents'
                ])
            ) {
                DB::commit();
                ResponseService::warningResponse("Data Stored successfully. But App push notification not send.");
            } else {
                DB::rollBack();
                ResponseService::logErrorResponse($e, "Exam Controller -> Store method");
                ResponseService::errorResponse();
            }
        }
    }

    public function show()
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenSendJson('exam-list');
        $offset = request('offset', 0);
        $limit = request('limit', 10);
        $sort = request('sort', 'id');
        $order = request('order', 'DESC');
        $search = request('search');
        $showDeleted = request('show_deleted');
        $medium_id = request('medium_id');
        $class_id = request('class_id');
        $schoolSettings = $this->cache->getSchoolSettings();
        $sessionYearId = $this->cache->getSessionYear()->id;

        $sql = $this->exam->builder()->with([
            'class.medium',
            'class.stream',
            'class.shift',
            'class.section'
        ])
            ->with([
                'timetable' => function ($q) {
                    $q->with('class_subject.subject')
                        ->with([
                            'exam_marks' => function ($query) {
                                $query->where('status', 1)
                                    ->with('user.student');
                            }
                        ]);
                }
            ])
            ->with([
                'class.section' => function ($q) {
                    $q->whereNull('class_sections.deleted_at');
                }
            ])
            ->where('session_year_id', $sessionYearId)
            ->when($search, function ($query) use ($search) {
                $query->where(function ($query) use ($search) {
                    $query->where('id', 'LIKE', "%$search%")
                        ->orWhere('name', 'LIKE', "%$search%")
                        ->orWhere('description', 'LIKE', "%$search%")
                        ->orWhere('created_at', 'LIKE', "%" . date('Y-m-d H:i:s', strtotime($search)) . "%")
                        ->orWhere('updated_at', 'LIKE', "%" . date('Y-m-d H:i:s', strtotime($search)) . "%")
                        ->orWhereHas('session_year', function ($subQuery) use ($search) {
                            $subQuery->where('name', 'LIKE', "%$search%");
                        });
                });
            })->when($medium_id, function ($query) use ($medium_id) {
                $query->whereHas('class', function ($q) use ($medium_id) {
                    $q->where('medium_id', $medium_id);
                });
            })->when($class_id, function ($query) use ($class_id) {
                $query->whereHas('class', function ($q) use ($class_id) {
                    $q->where('id', $class_id);
                });
            })
            ->when(!empty($showDeleted), function ($query) {
                $query->onlyTrashed();
            });

        $total = $sql->count();
        if ($offset >= $total && $total > 0) {
            $lastPage = floor(($total - 1) / $limit) * $limit; // calculate last page offset
            $offset = $lastPage;
        }
        $sql->orderBy($sort, $order)->skip($offset)->take($limit);
        $res = $sql->get();

        $bulkData = array();
        $bulkData['total'] = $total;
        $rows = array();
        $no = 1;

        $classSections = $this->classSection->builder()->get()->toArray();

        foreach ($res as $row) {
            $operate = '';

            if ($showDeleted) {
                $operate .= BootstrapTableService::menuRestoreButton('restore', route('exams.restore', $row->id));
                $operate .= BootstrapTableService::menuTrashButton('delete', route('exams.trash', $row->id));
            } else if ($row->publish == 0) {
                $operate .= BootstrapTableService::menuButton('timetable', route('exam.timetable.edit', $row->id));
                $operate .= BootstrapTableService::menuButton('publish', "#", ["publish-exam-result"], ['data-id' => $row->id]);

                if (($row->exam_status == 0)) {
                    $operate .= BootstrapTableService::menuEditButton('edit', route('exams.update', $row->id));
                }
                $operate .= BootstrapTableService::menuDeleteButton('delete', route('exams.destroy', $row->id));
            } else if ($row->publish == 1) {
                $operate .= BootstrapTableService::menuButton('timetable', route('exam.timetable.edit', $row->id));
                $operate .= BootstrapTableService::menuButton('Unpublish', "#", ["publish-exam-result"], ['data-id' => $row->id]);
            }

            $tempRow = $row->toArray();
            $tempRow['no'] = $no++;
            $classSectionWiseStatus = []; // Initialize here to accumulate all data for the current exam

            $sections = count($row->class->section ?? []) > 0 ? $row->class->section : [null];

            foreach ($sections as $section) {
                $sectionId = $section->id ?? null;
                $subjectWiseStatus = [];
                $processedSubjects = [];

                $classNameWithSection = $row->class->name ?? '' . ' - ' . ($section?->name ?? '') . ' ' . ($row->class->medium->name ?? '');

                $class_section_id = $this->findClassSection($classSections, $row->class_id, $sectionId);

                if ($row->has_timetable) {
                    foreach ($row->timetable as $timetable) {
                        $subject = $timetable->subject_with_name;
                        $subjectId = $timetable->class_subject_id;

                        if (isset($processedSubjects[$subjectId])) {
                            continue;
                        }
                        $marks = collect([]);
                        if ($class_section_id) {
                            $marks = $timetable->exam_marks->where('user.student.class_section_id', $class_section_id);
                        }
                        $marksSubmitted = $marks->isNotEmpty();
                        $status = $marksSubmitted ? 'Submitted' : 'Pending';

                        $subjectWiseStatus[] = [
                            'subject_id' => $subjectId,
                            'subject' => $subject,
                            'status' => $status,
                            'marks_count' => $marksSubmitted ? $marks->count() : 0,
                        ];

                        $processedSubjects[$subjectId] = true;
                    }

                    $classSectionStatus = count($subjectWiseStatus) > 0
                        ? (collect($subjectWiseStatus)->contains('status', 'Pending') ? 'Pending' : 'Submitted')
                        : 'Pending';


                    $classSectionWiseStatus[] = [
                        'class_section_name' => $classNameWithSection,
                        'status' => $classSectionStatus,
                        'subjectWiseStatus' => $subjectWiseStatus,
                    ];
                }
            }

            // ===================================

            // $sections = count($row->class->section ?? []) > 0 ? $row->class->section : [null];

            // foreach ($sections as $section) {
            //     $sectionId = $section?->id;
            //     $subjectWiseStatus = [];
            //     $processedSubjects = [];

            //     $classstreamname = ($classstreamname = $row->class?->stream?->name) ? ' (' . $classstreamname . ')' : ' ';
            //     $classshiftname = ($classshiftname = $row->class?->shift?->name) ? ' (' . $classshiftname . ')' : '';
            //     $classNameWithSection = $row->class->name . ' - ' . ($section?->name ?? '') . ' ' . ($row->class->medium->name ?? '') . $classstreamname . $classshiftname;

            //     // Get the class section ID for this section
            //     $class_section_id = $this->findClassSection($classSections, $row->class_id, $sectionId);

            //     if ($row->has_timetable) {
            //         foreach ($row->timetable as $timetable) {
            //             $subject = $timetable->subject_with_name;
            //             $subjectId = $timetable->class_subject_id;

            //             if (isset($processedSubjects[$subjectId])) {
            //                 continue;
            //             }

            //             // Filter marks by class section ID instead of section ID directly
            //             $marks = collect([]);
            //             if ($class_section_id) {
            //                 $marks = $timetable->exam_marks->where('user.student.class_section_id', $class_section_id);
            //             }

            //             $marksSubmitted = $marks->isNotEmpty();
            //             $status = $marksSubmitted ? 'Submitted' : 'Pending';

            //             $subjectWiseStatus[] = [
            //                 'subject_id' => $subjectId,
            //                 'subject' => $subject,
            //                 'status' => $status,
            //                 'marks_count' => $marksSubmitted ? $marks->count() : 0,
            //             ];

            //             $processedSubjects[$subjectId] = true;
            //         }

            //         $classSectionWiseStatus[] = [
            //             'class_section_name' => $classNameWithSection,
            //             'subject_wise_status' => $subjectWiseStatus,
            //         ];
            //     }
            // }

            $tempRow['classSectionWiseStatus'] = $classSectionWiseStatus;
            $tempRow['operate'] = BootstrapTableService::menuItem($operate);
            $tempRow['created_at'] = date($schoolSettings['date_format'], strtotime($row->created_at));
            $tempRow['updated_at'] = date($schoolSettings['date_format'], strtotime($row->updated_at));
            $rows[] = $tempRow;
        }
        $bulkData['rows'] = $rows;
        return response()->json($bulkData);
    }

    public function update($id, Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenSendJson('exam-edit');
        $request->validate(['name' => 'required']);
        try {
            $this->exam->update($id, $request->all());
            ResponseService::successResponse('Data Updated Successfully');
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e, "Exam Controller -> Update method");
            ResponseService::errorResponse();
        }
    }

    public function destroy($id)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenSendJson('exam-delete');
        try {
            $this->exam->deleteById($id);
            ResponseService::successResponse('Data Deleted Successfully');
        } catch (Throwable $e) {
            DB::rollBack();
            ResponseService::logErrorResponse($e, "Exam Controller -> Delete method");
            ResponseService::errorResponse();
        }
    }

    public function restore(int $id)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenSendJson('exam-delete');
        try {
            $this->exam->findOnlyTrashedById($id)->restore();
            ResponseService::successResponse("Data Restored Successfully");
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function trash($id)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenSendJson('exam-delete');
        try {
            $this->exam->findOnlyTrashedById($id)->forceDelete();
            ResponseService::successResponse("Data Deleted Permanently");
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e, "Exam Controller ->Trash Method", 'Can not Delete this because marks are already submitted');

            ResponseService::errorResponse();
        }
    }

    // -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    /*** Upload Marks ***/
    public function uploadMarks()
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenRedirect('exam-upload-marks');

        $sessionYearId = $this->cache->getSessionYear()->id;
        // $teacherId = Auth::user()->teacher->user_id;
        $teacherId = Auth::user()->id;
        $classes = $this->classSection->builder()->whereHas('class_teachers', function ($query) use ($teacherId, $sessionYearId) {
            $query->where('teacher_id', $teacherId)->where('session_year_id', $sessionYearId);
        })->with('class.stream', 'class.shift', 'section', 'medium')->orWhereHas('subject_teachers', function ($q) use ($teacherId, $sessionYearId) {
            $q->where('teacher_id', $teacherId)->where('session_year_id', $sessionYearId);
        })->get();

        $exams = $this->exam->builder()->with([
            'timetable' => function ($query) {
                $query->where('date', '<', date('Y-m-d'))->orWhere(function ($q) {
                    $q->whereDate('date', '=', date('Y-m-d'))->where('end_time', '<=', date('H:i:s'));
                })->with([
                    'class_subject' => function ($q) {
                        $q->SubjectTeacherClassTeacher()->with([
                            'subject_teacher' => function ($q) {
                                $q->where('teacher_id', Auth::user()->id)->with('subject');
                            }
                        ]);
                    }
                ]);
            }
        ])->where('publish', 0)->where('session_year_id', $sessionYearId)->get();


        return response()->view('exams.upload-marks', compact('exams', 'classes'));
    }

    public function marksList(Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');


        $request->validate(['class_section_id' => 'required', 'exam_id' => 'required', 'class_subject_id' => 'required',], ['class_section_id.required' => 'Class section field is required', 'exam_id.required' => 'Exam field is required', 'class_subject_id.required' => 'Class subject field is required',]);

        try {

            // Sorting and limit settings
            $sort = $request->input('sort', 'id');
            $order = $request->input('order', 'ASC');
            $search = $request->input('search');

            $sessionYear = $this->cache->getSessionYear();
            // Get Exam with timetable id and date to get exam status also
            $exam = $this->exam->builder()->with('timetable')->where('id', $request->exam_id)->where('session_year_id', $sessionYear->id)->firstOrFail();


            // Get Student ids according to Subject is elective or compulsory
            $classSubject = $this->classSubject->builder()->where('id', $request->class_subject_id)->withTrashed()->first();
            if ($classSubject->type == "Elective") {
                $studentIds = $this->studentSubject->builder()->where(['class_section_id' => $request->class_section_id, 'class_subject_id' => $classSubject->id])->pluck('student_id');
            } else {
                $studentIds = $this->users->builder()->role('Student')->whereHas('student', function ($query) use ($request) {
                    $query->where('class_section_id', $request->class_section_id);
                })->pluck('id');
            }

            // Get Timetable Data
            $timetable = $exam->timetable()->where('class_subject_id', $request->class_subject_id)->first();

            // IF Timetable is empty then show error message
            if (!$timetable) {
                return response()->json(['error' => true, 'message' => trans('Exam Timetable Does not Exists')]);
            }

            // IF Exam status is not 2 that is exam not completed then show error message
            if ($exam->exam_status != 2) {
                ResponseService::errorResponse('Exam not completed yet');
            }

            $sessionYear = $this->cache->getSessionYear(); // Get Students Data on the basis of Student ids
            $students = $this->users->builder()->role('Student')->whereIn('id', $studentIds)->with([
                'exam_marks' => function ($query) use ($timetable) {
                    $query->where('exam_timetable_id', $timetable->id);
                }
            ])
                ->when($search, function ($q) use ($search) {
                    $q->whereRaw("concat(first_name,' ',last_name) LIKE '%" . $search . "%'");
                })
                ->whereHas('student', function ($q) use ($sessionYear) {
                    $q->where('session_year_id', $sessionYear->id);
                })
                ->orderBy($sort, $order)->get();
            // Loop on the Students Data
            $rows = [];
            foreach ($students as $no => $student) {
                $rows[] = ['id' => $student->id, 'no' => $no + 1, 'student_name' => $student->full_name, 'total_marks' => $timetable->total_marks, 'exam_marks_id' => $student->exam_marks[0]->id ?? '', 'obtained_marks' => $student->exam_marks[0]->obtained_marks ?? '', 'status' => $student->exam_marks[0]->status ?? null, 'operate' => '<a href=' . route('exams.edit', $student->id) . ' class="btn btn-xs btn-gradient-primary btn-rounded btn-icon edit-data" data-id=' . $student->id . ' title="Edit" data-toggle="modal" data-target="#editModal"><i class="fa fa-edit"></i></a>&nbsp;&nbsp;<a href=' . route('exams.destroy', $student->id) . ' class="btn btn-xs btn-gradient-danger btn-rounded btn-icon delete-form" data-id=' . $student->id . '><i class="fa fa-trash"></i></a>',];
            }

            // Return Data as bulk-data
            $bulkData['rows'] = $rows;
            return response()->json($bulkData);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e, "Exam Controller -> Get Exam Subjects");
            ResponseService::errorResponse();
        }
    }

    public function submitMarks(Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        $request->validate(['exam_id' => 'required|numeric', 'class_subject_id' => 'required|numeric', 'exam_marks' => 'required|array',], ['class_id.required' => 'Class section field is required.', 'exam_id.required' => 'Exam field is required.', 'class_subject_id.required' => 'Subject field is required.', 'exam_marks.required' => 'No records found.',]);

        try {
            $sessionYearId = $this->cache->getSessionYear()->id;
            $exam_timetable = $this->examTimetable->builder()->where(['exam_id' => $request->exam_id, 'class_subject_id' => $request->class_subject_id, 'session_year_id' => $sessionYearId])->firstOrFail();

            foreach ($request->exam_marks as $examMarks) {
                $passing_marks = $exam_timetable->passing_marks;
                if ($examMarks['obtained_marks'] >= $passing_marks) {
                    $status = 1;
                } else {
                    $status = 0;
                }
                $marks_percentage = ($examMarks['obtained_marks'] / $examMarks['total_marks']) * 100;
                $exam_grade = findExamGrade($marks_percentage);

                if ($exam_grade == null) {
                    ResponseService::errorResponse('Grades data does not exists');
                }

                $this->examMarks->updateOrCreate(['id' => $examMarks['exam_marks_id'] ?? null], ['exam_timetable_id' => $exam_timetable->id, 'student_id' => $examMarks['student_id'], 'class_subject_id' => $request->class_subject_id, 'obtained_marks' => $examMarks['obtained_marks'], 'passing_status' => $status, 'session_year_id' => $exam_timetable->session_year_id, 'grade' => $exam_grade, 'status' => $request->status ?? 0]);
            }

            if ($request->status == 0) {
                ResponseService::successResponse('Data stored in draft successfully');
            }

            ResponseService::successResponse('Data stored and published successfully');
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e, "Exam Controller -> Get Exam Subjects");
            ResponseService::errorResponse();
        }
    }

    public function getSubjectByExam($exam_id, Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        try {
            $teacherId = Auth::user()->id;
            $sessionYearId = $this->cache->getSessionYear()->id;

            $isClassTeacher = ClassTeacher::where('teacher_id', $teacherId)->where('session_year_id', $sessionYearId)->where('class_section_id', $request->class_section_id)->first();


            if ($isClassTeacher) {
                $exam_timetable = ExamTimetable::with(['subject_teacher'])
                    ->with([
                        'class_subject' => function ($q) {
                            $q->withTrashed();
                        }
                    ])
                    ->where('exam_id', $exam_id)
                    ->get();
            } else {
                $exam_timetable = ExamTimetable::with([
                    'subject_teacher' => function ($q) use ($sessionYearId) {
                        $q->where('session_year_id', $sessionYearId);
                    }
                ])
                    ->with([
                        'class_subject' => function ($q) {
                            $q->withTrashed();
                        }
                    ])
                    ->whereHas('subject_teacher', function ($query) use ($teacherId, $request, $sessionYearId) {
                        $query->where('teacher_id', $teacherId)->where('session_year_id', $sessionYearId)->where('class_section_id', $request->class_section_id);
                    })
                    ->where('exam_id', $exam_id)
                    ->get();
            }

            $response = array('error' => false, 'message' => trans('data_fetch_successfully'), 'data' => $exam_timetable);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e, "Exam Controller -> Get Exam Subjects");
            ResponseService::errorResponse();
        }
        return response()->json($response);
    }


    // -----------------------------------------------------------------------------------------------------

    /*** Exam Result ***/

    public function getExamResultIndex()
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenRedirect('exam-result');
        $sessionYearId = $this->cache->getSessionYear()->id;
        $exams = $this->exam->builder()->with('class.medium')->where('publish', 1)->where('session_year_id', $sessionYearId);
        // $classSections = $this->classSection->all(['*'], ['class', 'class.stream', 'section', 'medium']);

        $classSections = $this->classSection->builder()->with('class.stream', 'class.shift', 'section', 'medium');
        if (Auth::user()->hasRole('Teacher')) {
            $classTeacher = $this->classTeacher->builder()->where(['teacher_id' => Auth::user()->id, 'session_year_id' => $sessionYearId])->with('class_section')->get();
            $classSections = $classSections->whereIn('id', $classTeacher->pluck('class_section_id')->toArray());
            $exams = $exams->whereIn('class_id', $classTeacher->pluck('class_id')->toArray());
        }

        $classSections = $classSections->get();
        $exams = $exams->get()->append(['prefix_name']);

        return view('exams.show_exam_result', compact('exams', 'classSections'));
    }

    public function showExamResult(Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenSendJson('exam-result');
        $offset = request('offset', 0);
        $limit = request('limit', 10);
        $sort = request('sort', 'id');
        $order = request('order', 'DESC');
        $search = request('search');
        $sessionYearId = $this->cache->getSessionYear()->id;

        $sql = $this->examResult->builder()->with([
            'user:id,first_name,last_name,email,image,school_id',
            'user.exam_marks' => function ($q) use ($request) {
                $q->whereHas('timetable', function ($q) use ($request) {
                    $q->where('exam_id', $request->exam_id);
                })->with('timetable', 'subject');
            }
        ])->where('exam_id', $request->exam_id)
            ->where('session_year_id', $sessionYearId)
            ->when($search, function ($q) use ($search, $request) {
                $q->where(function ($q) use ($search) {
                    $q->where('id', 'LIKE', "%$search%")
                        ->orwhere('total_marks', 'LIKE', "%$search%")
                        ->orwhere('grade', 'LIKE', "%$search%")
                        ->orwhere('obtained_marks', 'LIKE', "%$search%")
                        ->orwhere('percentage', 'LIKE', "%$search%")
                        ->orWhereHas('user', function ($q) use ($search) {
                            $q->whereRaw("concat(first_name,' ',last_name) LIKE '%" . $search . "%'");
                        });
                })->where('exam_id', $request->exam_id)->Owner();
            });



        if ($request->class_section_id) {
            $sql = $sql->where('class_section_id', $request->class_section_id);
        }

        $total = $sql->count();
        if ($offset >= $total && $total > 0) {
            $lastPage = floor(($total - 1) / $limit) * $limit; // calculate last page offset
            $offset = $lastPage;
        }
        $sql->orderBy($sort, $order)->skip($offset)->take($limit);
        $res = $sql->get();

        $bulkData = array();
        $bulkData['total'] = $total;
        $rows = array();
        $no = 1;
        foreach ($res as $row) {
            $operate = '';
            if (Auth::user()->can('exam-result-edit')) {
                $operate = BootstrapTableService::button('fa fa-edit', '#', ['btn-gradient-primary', 'btn-xs', 'btn-rounded', 'btn-icon', 'edit-data'], ['data-id' => $row->id, 'data-student_id' => $row->student_id, 'title' => 'Edit', 'data-toggle' => 'modal', 'data-target' => '#editModal']);

                $operate .= BootstrapTableService::button('fa fa-file-pdf-o', url('exams/result/student/') . '/' . $row->student_id . '/exam/' . $row->exam_id, ['btn-gradient-info', 'btn-xs', 'btn-rounded', 'btn-icon',], ['title' => __('view_result'), 'target' => '_blank']);
            }
            $tempRow = $row->toArray();
            $tempRow['no'] = $no++;
            $tempRow['operate'] = $operate;
            $rows[] = $tempRow;
        }

        $bulkData['rows'] = $rows;
        return response()->json($bulkData);
    }

    public function updateExamResultMarks(Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenSendJson('exam-result-edit');
        $request->validate([
            'edit.*.marks_id' => 'required|numeric',
            'edit.*.obtained_marks' => 'required|numeric|lte:edit.*.total_marks'
        ]);
        try {
            DB::beginTransaction();
            // Loop Through Request Data
            foreach ($request->edit as $data) {
                $passingMarks = $data['passing_marks']; // Get Passing Marks
                $marksPercentage = ($data['obtained_marks'] / $data['total_marks']) * 100; // Get Percentage

                // Get Percentage And Check that Grades Should not be NULL
                $grade = findExamGrade($marksPercentage);
                if ($grade == null) {
                    ResponseService::errorResponse("Grades data does not exists");
                }

                // Array for Update Marks
                $updateMarksData = array(
                    'obtained_marks' => $data['obtained_marks'],
                    'passing_status' => $data['obtained_marks'] >= $passingMarks ? 1 : 0,
                    'grade' => $grade
                );

                $this->examMarks->update($data['marks_id'], $updateMarksData); // Update Exam Marks

                $examResultId = $this->examResult->builder()->where(['exam_id' => $data['exam_id'], 'student_id' => $data['student_id']])->value('id'); // Get Exam Result ID

                // Query Data From Exam Table To Get Exam Marks According to Exam ID

                DB::enableQueryLog();
                $exam = $this->exam->builder()->with([
                    'marks' => function ($query) use ($data) {
                        $query->with('user.student:id,user_id,class_section_id')
                            ->selectRaw('SUM(obtained_marks) as total_obtained_marks,student_id')
                            ->selectRaw('SUM(total_marks) as total_marks')
                            ->selectRaw('MIN(CASE WHEN passing_status = 0 THEN 0 ELSE 1 END) as overall_passing_status')
                            ->where('student_id', $data['student_id'])
                            ->groupBy('student_id');
                    },
                    'timetable' => function ($query) use ($data) {
                        $query->where(['exam_id' => $data['exam_id']]);
                    }
                ])->where('id', $data['exam_id'])->first();

                // Loop through Exam Marks Data
                foreach ($exam->marks as $examMarks) {
                    $percentage = ($examMarks['total_obtained_marks'] * 100) / $examMarks['total_marks']; // Get Percentage

                    // Get Percentage And Check that Grades Should not be NULL
                    $grade = findExamGrade($percentage);
                    if ($grade == null) {
                        ResponseService::errorResponse("Grades data does not exists");
                    }

                    // Array For Update Exam Result Data
                    $examResultData = array("obtained_marks" => $examMarks['total_obtained_marks'], "percentage" => round($percentage, 2), "grade" => $grade, "status" => $examMarks['overall_passing_status']);

                    $this->examResult->update($examResultId, $examResultData); // Update Exam Result
                }
            }
            DB::commit();
            ResponseService::successResponse("Data Updated Successfully");
        } catch (Throwable $e) {
            DB::rollBack();
            ResponseService::logErrorResponse($e, "Exam Controller -> updateExamResultMarks method");
            ResponseService::errorResponse();
        }
    }

    // -----------------------------------------------------------------------------------------------------

    public function publishExamResult($id)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        try {

            // Get The Exam Data with Marks and Timetable
            $exam = $this->exam->builder()->with([
                'marks' => function ($query) {
                    $query->with('user:id,first_name,last_name,image', 'user.student:id,user_id,class_section_id')->selectRaw('SUM(obtained_marks) as total_obtained_marks, student_id')->selectRaw('SUM(total_marks) as total_marks')->groupBy('student_id');
                },
                'timetable:id,exam_id,start_time,end_time'
            ])->with([
                'timetable' => function ($q) {
                    $q->with([
                        'exam_marks' => function ($query) {
                            $query->where('status', 1);
                        }
                    ]);
                }
            ])->findOrFail($id);

            $allSubjectsSubmitted = true;
            foreach ($exam->timetable as $timetable) {
                // Check if there are no exam marks associated with this timetable
                if ($timetable->exam_marks->isEmpty()) {
                    // If marks for any subject are missing, set the flag to false and break the loop
                    $allSubjectsSubmitted = false;
                    break;
                }
            }

            if (!$allSubjectsSubmitted) {
                ResponseService::errorResponse("Marks are not uploaded yet.");
            }



            DB::beginTransaction();
            if ($exam->exam_status == 2 && $exam->marks->isNotEmpty()) {

                if ($exam->publish == 0) {


                    // If exam is Unpublished then Insert ExamResult records and Publish the Exam
                    $examResult = $exam->marks->map(function ($examMarks) use ($exam, $id) {
                        $percentage = ($examMarks['total_obtained_marks'] * 100) / $examMarks['total_marks'];
                        $grade = findExamGrade($percentage);

                        if ($grade === null) {
                            ResponseService::errorResponse("Grades data does not exists");
                        }

                        // Get passing status
                        $status = $this->resultStatus($id, $examMarks['student_id']);

                        $data = [
                            'exam_id' => $exam->id,
                            'class_section_id' => $examMarks['user']['student']['class_section_id'],
                            'student_id' => $examMarks['student_id'],
                            'total_marks' => $examMarks['total_marks'],
                            'obtained_marks' => $examMarks['total_obtained_marks'],
                            'percentage' => round($percentage, 2),
                            'grade' => $grade,
                            'status' => $status,
                            'session_year_id' => $exam->session_year_id
                        ];
                        return $data;
                    });

                    $studentIds = $examResult->pluck('student_id')->toArray();
                    $guardian_id = $this->student->builder()->with('user')->whereIn('user_id', $studentIds)->pluck('guardian_id')->toArray();

                    $this->examResult->createBulk($examResult->toArray()); // Add Data in Exam Result
                    $this->exam->update($id, ['publish' => 1]); // Update Exam with Publish status 1

                    $user = array_merge($studentIds, $guardian_id);

                    $title = 'Result Publish for ' . $exam->name . ' examinations !!!';
                    $body = 'Congrats your result has been publish Click here see your result ';
                    $type = 'exam result';

                    // -------------------------------------------------------
                    // NEW LOGIC — Correct Result IDs per Student/User
                    // -------------------------------------------------------

                    // Get mapping of student_id => result_id
                    $resultMap = ExamResult::where('exam_id', $exam->id)
                        ->whereIn('student_id', $studentIds)
                        ->pluck('id', 'student_id')
                        ->toArray();  // [student_id => result_id]

                    // Send individual notifications
                    foreach ($user as $userId) {

                        // Their specific result_id
                        $resultId = $resultMap[$userId] ?? null;
                        if (!$resultId)
                            continue;

                        // Custom data for this specific user
                        $customData = [
                            'exam_id' => $exam->id,
                            'result_id' => $resultId
                        ];

                        // Send notification to this user only
                        send_notification([$userId], $title, $body, $type, $customData);
                    }
                } else {
                    ExamResult::where('exam_id', $id)->delete(); // If Exam is already published then unpublished it and delete Exam Result
                    $this->exam->update($id, ['publish' => 0]); // Update Exam with Publish status 0
                }
                DB::commit();
                ResponseService::successResponse('Data Stored Successfully');
            } else {
                ResponseService::errorResponse('Exam not completed yet');
            }
        } catch (Throwable $e) {

            if (
                Str::contains($e->getMessage(), [
                    'does not exist',
                    'file_get_contents'
                ])
            ) {
                DB::commit();
                ResponseService::warningResponse("Data Stored successfully. But App push notification not send.");
            } else {
                DB::rollBack();
                ResponseService::logErrorResponse($e, "Exam Controller -> publishExamResult method");
                ResponseService::errorResponse();
            }
        }
    }

    public function resultStatus($exam_id, $student_id)
    {

        $status = $this->examMarks->builder()->whereHas('timetable', function ($q) use ($exam_id) {
            $q->where('exam_id', $exam_id);
        })->where('student_id', $student_id)->where('passing_status', 0)->first();

        if ($status) {
            return 0;
        }
        return 1;
    }

    public function deleteExamTimetable($id)
    {
        ResponseService::noPermissionThenSendJson('exam-timetable-delete');
        try {
            DB::beginTransaction();
            $this->examTimetable->deleteById($id);
            DB::commit();
            ResponseService::successResponse('Data Deleted Successfully');
        } catch (Throwable $e) {
            DB::rollBack();
            ResponseService::logErrorResponse($e, "Exam Controller -> DeleteTimetable method", trans('cannot_delete_because_data_is_associated_with_other_data'));
            ResponseService::errorResponse();
        }
    }

    public function resultReport($session_year_id, $exam_name)
    {
        try {

            $exams = $this->exam->builder()->select('id', 'name', 'class_id', 'session_year_id', 'publish')->with('class.medium')->where('session_year_id', $session_year_id)->where('publish', 1)->where('name', $exam_name)->withCount('results as total_students')->withCount([
                'results as pass_students' => function ($q) {
                    $q->where('status', 1);
                }
            ])->get()->makeHidden(['exam_status', 'exam_status_name', 'has_timetable']);

            ResponseService::successResponse('Data Fetched Successfully', $exams);
        } catch (Throwable $e) {
            DB::rollBack();
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function examTimetableIndex()
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noAnyPermissionThenRedirect(['exam-upload-marks', 'exam-timetable-list']);
        try {

            $class_sections = $this->classSection->builder()->with('class.stream', 'class.shift', 'medium')->get()->pluck('full_name', 'class_id');

            $sessionYear = $this->cache->getSessionYear();
            $class_id = array_keys($class_sections->toArray());
            $exams = $this->exam->builder()->whereIn('class_id', $class_id)->where('session_year_id', $sessionYear->id)->get();

            return view('exams.exams_timetable', compact('class_sections', 'exams'));
        } catch (Throwable $e) {
            DB::rollBack();
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function examTimetableShow(Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noAnyPermissionThenRedirect(['exam-upload-marks', 'exam-timetable-list']);
        try {


            $sql = $this->examTimetable->builder()->with('class_subject.subject')
                ->where('exam_id', $request->exam_id)
                ->orderBy('date', 'ASC')->orderBy('start_time', 'ASC');
            $total = $sql->count();

            $res = $sql->get();
            $bulkData = array();
            $bulkData['total'] = $total;
            $rows = array();
            $no = 1;
            foreach ($res as $row) {
                $tempRow = $row->toArray();
                $tempRow['no'] = $no++;
                $rows[] = $tempRow;
            }

            $bulkData['rows'] = $rows;
            return response()->json($bulkData);
        } catch (Throwable $e) {
            DB::rollBack();
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function examResultPdf($student_id, $exam_id)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenRedirect('exam-result');

        try {
            $results = $this->examResult->builder()
                ->with([
                    'exam',
                    'session_year',
                    'class_section.class.stream',
                    'class_section.section',
                    'class_section.medium',
                    'user' => function ($q) use ($exam_id) {
                        $q->with([
                            'student.guardian',
                            'exam_marks' => function ($q) use ($exam_id) {
                                $q->whereHas('timetable', function ($q) use ($exam_id) {
                                    $q->where('exam_id', $exam_id);
                                })->with([
                                    'class_subject' => function ($q) {
                                        $q->withTrashed()->with('subject:id,name,type');
                                    },
                                    'timetable'
                                ]);
                            }
                        ]);
                    }
                ])
                ->where('exam_id', $exam_id)
                ->select('exam_results.*')
                ->get();

            // Convert the results to a collection
            $results = collect($results);

            // Add rank calculation to each item in the collection
            $results = $results->map(function ($result) {
                $rank = DB::table('exam_results as er2')
                    ->where('er2.class_section_id', $result->class_section_id)
                    ->where('er2.obtained_marks', '>', $result->obtained_marks)
                    ->where('er2.exam_id', $result->exam_id)
                    ->where('er2.status', 1)
                    ->distinct('er2.obtained_marks')
                    ->count() + 1;

                $result->rank = $rank;
                return $result;
            });

            // Filter the collection based on student ID
            $result = $results->where('student_id', $student_id)->first();

            if (!$result) {
                return redirect()->back()->with('error', trans('no_records_found'));
            }

            $grades = $this->grade->builder()->orderBy('starting_range', 'ASC')->get();


            $settings = $this->cache->getSchoolSettings();
            $data = explode("storage/", $settings['horizontal_logo'] ?? '');
            $settings['horizontal_logo'] = end($data);

            if ($settings['horizontal_logo'] == null) {
                $systemSettings = $this->cache->getSystemSettings();
                $data = explode("storage/", $systemSettings['horizontal_logo'] ?? '');
                $settings['horizontal_logo'] = end($data);
            }


            $pdf = PDF::loadView('exams.exam_result_pdf', compact('result', 'settings', 'grades'));


            return $pdf->stream();
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function bulkUploadIndex()
    {
        ResponseService::noFeatureThenRedirect('Exam Management');

        $teacherId = Auth::user()->teacher->user_id;
        $sessionYearId = $this->cache->getSessionYear()->id;

        // Fetch classes where the teacher is a class teacher or subject teacher
        $classes = $this->classSection->builder()
            ->whereHas('class_teachers', function ($query) use ($teacherId, $sessionYearId) {
                $query->where('teacher_id', $teacherId)->where('session_year_id', $sessionYearId);
            })
            ->orWhereHas('subject_teachers', function ($query) use ($teacherId, $sessionYearId) {
                $query->where('teacher_id', $teacherId)->where('session_year_id', $sessionYearId);
            })
            ->with('class.stream', 'class.shift', 'section', 'medium', 'subjects')
            ->get();

        $exams = $this->exam->builder()->with([
            'timetable' => function ($query) {
                $query->where('date', '<', date('Y-m-d'))->orWhere(function ($q) {
                    $q->whereDate('date', '=', date('Y-m-d'))->where('end_time', '<=', date('H:i:s'));
                })->with([
                    'class_subject' => function ($q) {
                        $q->SubjectTeacherClassTeacher()->with([
                            'subject_teacher' => function ($q) {
                                $q->where('teacher_id', Auth::user()->id)->with('subject');
                            }
                        ]);
                    }
                ]);
            }
        ])->where('publish', 0)->where('session_year_id', $sessionYearId)->get();

        return response(view('exams.bulk_upload_marks', compact('classes', 'exams')));
    }

    public function downloadSampleFile(Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');

        $validator = Validator::make($request->all(), [
            'class_section_id' => 'required|numeric',
            'exam_id' => 'required',
            'class_subject_id' => 'required',
        ]);
        if ($validator->fails()) {
            ResponseService::errorResponse($validator->errors()->first());
        }

        try {
            $sessionYearId = $this->cache->getSessionYear()->id;

            $exam = $this->exam->builder()->with('timetable')->where('id', $request->exam_id)->where('session_year_id', $sessionYearId)->first();

            // Get Student ids according to Subject is elective or compulsory
            $classSubject = $this->classSubject->builder()->where('id', $request->class_subject_id)->withTrashed()->first();


            if ($classSubject->type == "Elective") {
                $studentIds = $this->studentSubject->builder()->where(['class_section_id' => $request->class_section_id, 'class_subject_id' => $classSubject->id])->pluck('student_id');
            } else {
                $studentIds = $this->users->builder()->role('Student')->whereHas('student', function ($query) use ($request) {
                    $query->where('class_section_id', $request->class_section_id);
                })->pluck('id');
            }

            // Get Timetable Data
            $timetable = $exam->timetable()->where('class_subject_id', $request->class_subject_id)->first();

            // IF Timetable is empty then show error message
            if (!$timetable) {
                return redirect()->route('exam.bulk-upload-marks')->with('error', trans('Exam Timetable Does not Exists'));
            }

            // IF Exam status is not 2 that is exam not completed then show error message
            if ($exam->exam_status != 2) {

                ResponseService::errorRedirectResponse(null, 'Exam not completed yet');
            }

            $sessionYear = $this->cache->getSessionYear();
            $students = $this->users->builder()->role('Student')->whereIn('id', $studentIds)->with([
                'student' => function ($query) use ($sessionYear) {
                    $query->where('session_year_id', $sessionYear->id);
                },
                'exam_marks' => function ($query) use ($timetable) {
                    $query->where('exam_timetable_id', $timetable->id);
                }
            ])->get();
            $data = [];
            // Loop on the Students Data
            foreach ($students as $student) {
                $data[] = [
                    'exam_marks_id' => $student->exam_marks[0]->id ?? '',
                    'student_id' => $student->id,
                    'student_name' => $student->full_name,
                    'total_marks' => $timetable->total_marks,
                    'obtained_marks' => $student->exam_marks[0]->obtained_marks ?? '',
                ];
            }

            // Create a file name using Class Section, Exam Name, and Subject Name
            $classSection = $this->classSection->builder()->with('class', 'class.stream', 'class.shift', 'section', 'medium')->where('id', $request->class_section_id)->first()->full_name;
            $examName = $exam->name;
            $subjectName = $classSubject->subject->name;

            $file_name = $classSection . '_' . $examName . '_' . $subjectName . '_marks_bulk_upload.xlsx';

            $file_name = str_replace(['/', '\\'], '-', $file_name);
            $file_name = preg_replace('/[^A-Za-z0-9_\-\.]/', '_', $file_name);

            return Excel::download(new MarksDataExport($data), $file_name);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e, 'Exam Controller ---> Download Sample File');
            ResponseService::errorResponse();
        }
    }

    public function storeBulkData(Request $request)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');

        $validator = Validator::make($request->all(), [
            'class_section_id' => 'required|numeric',
            'exam_id' => 'required',
            'class_subject_id' => 'required',
            'file' => 'required|mimes:csv,txt'
        ]);
        if ($validator->fails()) {
            ResponseService::errorResponse($validator->errors()->first());
        }
        try {
            Excel::import(new MarksDataImport($request->class_section_id, $request->exam_id, $request->class_subject_id), $request->file);

            ResponseService::successResponse('Data Stored Successfully');
        } catch (ValidationException $e) {
            ResponseService::errorResponse($e->getMessage());
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e, "Exam Controller -> Store Bulk method");
            ResponseService::errorResponse();
        }
    }

    public function viewMarksindex()
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        ResponseService::noPermissionThenRedirect('exam-create');
        $classes = $this->class->all(['*'], ['stream', 'medium', 'shift']);
        $subjects = $this->subject->builder()->orderBy('id', 'DESC')->get();
        $mediums = $this->mediums->builder()->pluck('name', 'id');
        return response(view('exams.view_marks', compact('classes', 'subjects', 'mediums')));
    }

    public function viewMarksShow()
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        $offset = request('offset', 0);
        $limit = request('limit', 10);
        $sort = request('sort', 'id');
        $order = request('order', 'DESC');
        $search = request('search');
        $medium_id = request('medium_id');
        $schoolSettings = $this->cache->getSchoolSettings();
        $sessionYearId = $this->cache->getSessionYear()->id;

        $sql = $this->exam->builder()->with([
            'class.medium',
            'class.stream',
            'class.shift',
            'timetable.class_subject' => function ($q) {
                $q->withTrashed();
            }
        ])
            ->with([
                'timetable' => function ($q) {
                    $q->with('class_subject.subject')
                        ->with([
                            'exam_marks' => function ($query) {
                                // $query->where('status', 1)
                                //     ->with('user.student');
                            }
                        ]);
                }
            ])
            ->with([
                'class.section' => function ($q) {
                    $q->whereNull('class_sections.deleted_at');
                }
            ])
            ->where('session_year_id', $sessionYearId)
            ->when($search, function ($query) use ($search) {
                $query->where(function ($query) use ($search) {
                    $query->where('id', 'LIKE', "%$search%")
                        ->orWhere('name', 'LIKE', "%$search%")
                        ->orWhere('description', 'LIKE', "%$search%")
                        ->orWhere('created_at', 'LIKE', "%" . date('Y-m-d H:i:s', strtotime($search)) . "%")
                        ->orWhere('updated_at', 'LIKE', "%" . date('Y-m-d H:i:s', strtotime($search)) . "%")
                        ->orWhereHas('session_year', function ($subQuery) use ($search) {
                            $subQuery->where('name', 'LIKE', "%$search%");
                        });
                });
            })->when($medium_id, function ($query) use ($medium_id) {
                $query->whereHas('class', function ($q) use ($medium_id) {
                    $q->where('medium_id', $medium_id);
                });
            })
            ->when(!empty($showDeleted), function ($query) {
                $query->onlyTrashed();
            });

        $total = $sql->count();
        if ($offset >= $total && $total > 0) {
            $lastPage = floor(($total - 1) / $limit) * $limit; // calculate last page offset
            $offset = $lastPage;
        }
        $sql->orderBy($sort, $order)->skip($offset)->take($limit);
        $res = $sql->get();

        $bulkData = [];
        $bulkData['total'] = $total;
        $rows = [];
        $no = 1;

        $classSections = $this->classSection->builder()->get()->toArray();

        foreach ($res as $row) {
            $tempRow = $row->toArray();
            $tempRow['no'] = $no++;
            $classSectionWiseStatus = [];

            $sections = count($row->class->section ?? []) > 0 ? $row->class->section : [null];

            foreach ($sections as $section) {
                $sectionId = $section?->id;
                $subjectWiseStatus = [];
                $processedSubjects = [];

                $classstreamname = ($classstreamname = $row->class?->stream?->name) ? ' (' . $classstreamname . ')' : ' ';
                $classshiftname = ($classshiftname = $row->class?->shift?->name) ? ' (' . $classshiftname . ')' : '';
                $classNameWithSection = $row->class->name . ' - ' . ($section?->name ?? '') . ' ' . ($row->class->medium->name ?? '') . $classstreamname . $classshiftname;

                // Get the class section ID for this section
                $class_section_id = $this->findClassSection($classSections, $row->class_id, $sectionId);

                if ($row->has_timetable) {
                    foreach ($row->timetable as $timetable) {
                        $subject = $timetable->subject_with_name;
                        $subjectId = $timetable->class_subject_id;

                        if (isset($processedSubjects[$subjectId])) {
                            continue;
                        }

                        // Filter marks by class section ID instead of section ID directly
                        $marks = collect([]);
                        if ($class_section_id) {
                            $marks = $timetable->exam_marks->where('user.student.class_section_id', $class_section_id);
                        }

                        $marksSubmitted = $marks->isNotEmpty();
                        $status = $marksSubmitted ? 'Submitted' : 'Pending';

                        $subjectWiseStatus[] = [
                            'subject_id' => $subjectId,
                            'subject' => $subject,
                            'status' => $status,
                            'marks_count' => $marksSubmitted ? $marks->count() : 0,
                        ];

                        $processedSubjects[$subjectId] = true;
                    }

                    $classSectionWiseStatus[] = [
                        'class_section_name' => $classNameWithSection,
                        'subject_wise_status' => $subjectWiseStatus,
                    ];
                }
            }


            $tempRow['classSectionWiseStatus'] = $classSectionWiseStatus;
            $tempRow['created_at'] = date($schoolSettings['date_format'], strtotime($row->created_at));
            $tempRow['updated_at'] = date($schoolSettings['date_format'], strtotime($row->updated_at));
            $rows[] = $tempRow;
        }
        $bulkData['rows'] = $rows;
        return response()->json($bulkData);
    }

    public function findClassSection($classSections, $class_id, $sectionId)
    {
        foreach ($classSections as $classSection) {
            if ($classSection['class_id'] == $class_id && $classSection['section_id'] == $sectionId) {
                return $classSection['id'];
            }
        }
    }

    public function getExamByClassId($class_section_id)
    {
        ResponseService::noFeatureThenRedirect('Exam Management');
        try {
            $sessionYear = $this->cache->getSessionYear();
            $class_id = ClassSection::where('id', $class_section_id)->value('class_id');


            $exams = $this->exam->builder()->where(['class_id' => $class_id, 'session_year_id' => $sessionYear->id])->with([
                'timetable' => function ($query) {
                    $query->where('date', '<', date('Y-m-d'))->orWhere(function ($q) {
                        $q->whereDate('date', '=', date('Y-m-d'))->where('end_time', '<=', date('H:i:s'));
                    });
                }
            ])->where('publish', 0)->get();

            ResponseService::successResponse('Data Fetched Successfully', $exams);
        } catch (Throwable $e) {

            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }
}