File "ApiController.php"

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

<?php

namespace App\Http\Controllers\Api;


use App\Events\MessageSent;
use App\Http\Controllers\Controller;
use App\Models\Chat;
use App\Models\ClassTeacher;
use App\Models\Role;
use App\Models\Students;
use App\Models\SubjectTeacher;
use App\Models\User;
use App\Models\TransportationPayment;
use App\Models\RouteVehicle;
use App\Models\TransportationFee;
use App\Repositories\Attachment\AttachmentInterface;
use App\Repositories\Chat\ChatInterface;
use App\Repositories\ClassSection\ClassSectionInterface;
use App\Repositories\ExamResult\ExamResultInterface;
use App\Repositories\Gallery\GalleryInterface;
use App\Repositories\Grades\GradesInterface;
use App\Repositories\Holiday\HolidayInterface;
use App\Repositories\Leave\LeaveInterface;
use App\Repositories\LeaveDetail\LeaveDetailInterface;
use App\Repositories\LeaveMaster\LeaveMasterInterface;
use App\Repositories\Medium\MediumInterface;
use App\Repositories\PaymentConfiguration\PaymentConfigurationInterface;
use App\Repositories\PaymentTransaction\PaymentTransactionInterface;
use App\Repositories\SchoolSetting\SchoolSettingInterface;
use App\Repositories\SessionYear\SessionYearInterface;
use App\Repositories\Student\StudentInterface;
use App\Repositories\User\UserInterface;
use App\Services\CachingService;
use App\Services\FcmTokenService;
use App\Services\Payment\PaymentService;
use App\Services\ResponseService;
use App\Repositories\Fees\FeesInterface;
use App\Repositories\ExtraFormField\ExtraFormFieldsInterface;
use Auth;
use DB;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use PDF;
use Stripe\Exception\ApiErrorException;
use Throwable;
use App\Repositories\Files\FilesInterface;
use App\Repositories\Message\MessageInterface;
use App\Repositories\Transportation\PickupPointRepositoryInterface;
use App\Rules\MaxFileSize;
use App\Services\GeneralFunctionService;
use Laravel\Sanctum\PersonalAccessToken;
use App\Models\School;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use App\Models\Notification;
use App\Models\UserNotification;
use App\Models\ClassSubject;
use App\Models\Message;
use App\Models\Subject;
use Exception;
use GuzzleHttp\Psr7\UploadedFile;

class ApiController extends Controller
{
    private CachingService $cache;
    private HolidayInterface $holiday;
    private StudentInterface $student;
    private PaymentConfigurationInterface $paymentConfiguration;
    private PaymentTransactionInterface $paymentTransaction;
    private GalleryInterface $gallery;
    private SessionYearInterface $sessionYear;
    private LeaveDetailInterface $leaveDetail;
    private LeaveMasterInterface $leaveMaster;
    private LeaveInterface $leave;
    private UserInterface $user;
    private MediumInterface $medium;
    private ClassSectionInterface $classSection;
    private FilesInterface $files;
    private ExamResultInterface $examResult;
    private GradesInterface $grade;
    private ChatInterface $chat;
    private MessageInterface $message;
    private AttachmentInterface $attachment;
    private SchoolSettingInterface $schoolSettings;
    private FeesInterface $fees;
    private PickupPointRepositoryInterface $pickupPoint;
    private ExtraFormFieldsInterface $extraFormFields;


    public function __construct(CachingService $cache, HolidayInterface $holiday, StudentInterface $student, PaymentConfigurationInterface $paymentConfiguration, PaymentTransactionInterface $paymentTransaction, GalleryInterface $gallery, SessionYearInterface $sessionYear, LeaveDetailInterface $leaveDetail, LeaveMasterInterface $leaveMaster, LeaveInterface $leave, UserInterface $user, MediumInterface $medium, ClassSectionInterface $classSection, ExamResultInterface $examResult, GradesInterface $grade, FilesInterface $files, ChatInterface $chat, MessageInterface $message, AttachmentInterface $attachment, SchoolSettingInterface $schoolSettings, FeesInterface $fees, PickupPointRepositoryInterface $pickupPoint, ExtraFormFieldsInterface $extraFormFields)
    {
        $this->cache = $cache;
        $this->holiday = $holiday;
        $this->student = $student;
        $this->paymentConfiguration = $paymentConfiguration;
        $this->paymentTransaction = $paymentTransaction;
        $this->gallery = $gallery;
        $this->sessionYear = $sessionYear;
        $this->leaveDetail = $leaveDetail;
        $this->leaveMaster = $leaveMaster;
        $this->leave = $leave;
        $this->user = $user;
        $this->medium = $medium;
        $this->classSection = $classSection;
        $this->files = $files;
        $this->examResult = $examResult;
        $this->grade = $grade;
        $this->chat = $chat;
        $this->message = $message;
        $this->attachment = $attachment;
        $this->schoolSettings = $schoolSettings;
        $this->fees = $fees;
        $this->pickupPoint = $pickupPoint;
        $this->extraFormFields = $extraFormFields;
    }

    public function logout(Request $request)
    {
        try {
            $user = $request->user();

            // Remove FCM token for the specific device (if device_id or fcm_token provided)
            // Otherwise, remove all tokens for the user (full logout)
            $fcmTokenService = app(FcmTokenService::class);

            if ($request->device_id) {
                // Remove token for specific device
                $fcmTokenService->removeToken($user, null, $request->device_id);
            } elseif ($request->fcm_id) {
                // Remove specific FCM token
                $fcmTokenService->removeToken($user, $request->fcm_id);
            } else {
                // Remove all tokens for the user (full logout)
                $fcmTokenService->removeToken($user);
            }

            // Backward compatibility: Clear old fcm_id column
            $user->fcm_id = '';
            if (isset($user->web_fcm)) {
                $user->web_fcm = '';
            }
            $user->save();

            // Delete the access token
            $token = $request->bearerToken();
            if ($token) {
                $accessToken = PersonalAccessToken::findToken($token);
                if ($accessToken) {
                    $accessToken->delete();
                }
            }

            ResponseService::successResponse('Logout Successfully done');
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getHolidays(Request $request)
    {
        try {
            // $query->whereDate('date', '>=',$sessionYear->start_date)
            //     ->whereDate('date', '<=',$sessionYear->end_date);

            $sessionYear = $this->cache->getDefaultSessionYear();
            $settings = $this->cache->getSchoolSettings();
            if ($request->child_id) {
                $child = $this->student->findById($request->child_id);
                $data = $this->holiday->builder()->where('school_id', $child->user->school_id);
            } else {
                $data = $this->holiday->builder();
            }

            $sessionyearStartDate = carbon::createFromFormat($settings['date_format'], $sessionYear->start_date)->format('Y-m-d');
            $sessionyearEndDate = carbon::createFromFormat($settings['date_format'], $sessionYear->end_date)->format('Y-m-d');
            $data = $data->whereDate('date', '>=', $sessionyearStartDate)
                ->whereDate('date', '<=', $sessionyearEndDate)->get();

            ResponseService::successResponse("Holidays Fetched Successfully", $data);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getSettings(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'type' => 'required|in:student_parent_privacy_policy,teacher_staff_privacy_policy,student_terms_condition,teacher_terms_condition,contact_us,about_us,app_settings,fees_settings,terms_condition,privacy_policy'
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            $systemSettings = $this->cache->getSystemSettings();
            if ($request->type == "app_settings") {
                $data = array(
                    'app_link' => $systemSettings['app_link'] ?? "",
                    'ios_app_link' => $systemSettings['ios_app_link'] ?? "",
                    'app_version' => $systemSettings['app_version'] ?? "",
                    'ios_app_version' => $systemSettings['ios_app_version'] ?? "",
                    'force_app_update' => $systemSettings['force_app_update'] ?? "",
                    'app_maintenance' => $systemSettings['app_maintenance'] ?? "",
                    'teacher_app_link' => $systemSettings['teacher_app_link'] ?? "",
                    'teacher_ios_app_link' => $systemSettings['teacher_ios_app_link'] ?? "",
                    'teacher_app_version' => $systemSettings['teacher_app_version'] ?? "",
                    'teacher_ios_app_version' => $systemSettings['teacher_ios_app_version'] ?? "",
                    'teacher_force_app_update' => $systemSettings['teacher_force_app_update'] ?? "",
                    'teacher_app_maintenance' => $systemSettings['teacher_app_maintenance'] ?? "",
                    'tagline' => $systemSettings['tag_line'] ?? "",
                    'title' => $systemSettings['system_name'] ?? "",
                );
            } else {
                $data = isset($systemSettings[$request->type]) ? htmlspecialchars_decode($systemSettings[$request->type]) : "";
            }
            ResponseService::successResponse("Data Fetched Successfully", $data);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    protected function forgotPassword(Request $request)
    {
        $request->validate([
            'email' => "required|email",
            'school_code' => 'required'
        ]);
        try {

            $schoolCode = $request->school_code;

            if ($schoolCode) {
                $school = School::on('mysql')->where('code', $schoolCode)->first();
                if ($school) {
                    DB::setDefaultConnection('school');
                    Config::set('database.connections.school.database', $school->database_name);
                    DB::purge('school');
                    DB::connection('school')->reconnect();
                    DB::setDefaultConnection('school');

                    $response = Password::sendResetLink(['email' => $request->email]);

                    if ($response == Password::RESET_LINK_SENT) {
                        ResponseService::successResponse("Forgot Password email send successfully");
                    } else {
                        ResponseService::errorResponse("Cannot send Reset Password Link.Try again later", null, config('constants.RESPONSE_CODE.RESET_PASSWORD_FAILED'));
                    }
                } else {
                    return response()->json(['error' => true, 'message' => 'Invalid school code'], 200);
                }
            } else {
                return response()->json(['error' => true, 'message' => 'Unauthenticated'], 200);
            }
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    protected function changePassword(Request $request)
    {

        $validator = Validator::make($request->all(), [
            'current_password' => 'required',
            'new_password' => 'required|min:6',
            'new_confirm_password' => 'same:new_password',
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }

        try {
            $user = $request->user();
            if (Hash::check($request->current_password, $user->password)) {
                $user->update(['password' => Hash::make($request->new_password)]);
                ResponseService::successResponse("Password Changed successfully.");
            } else {
                ResponseService::errorResponse("Invalid Password", null, config('constants.RESPONSE_CODE.INVALID_PASSWORD'));
            }
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getPaymentMethod(Request $request)
    {
        if (Auth::user()->hasRole('Guardian')) {
            $validator = Validator::make($request->all(), [
                'child_id' => 'required|numeric',
            ]);
            if ($validator->fails()) {
                ResponseService::validationError($validator->errors()->first());
            }
        }
        try {

            $response = $this->paymentConfiguration->builder()->select('payment_method', 'status')->pluck('status', 'payment_method');
            ResponseService::successResponse("Payment Details Fetched", $response);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getPaymentConfirmation(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'id' => 'required'
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            $paymentTransaction = app(PaymentTransactionInterface::class)->builder()->where('id', $request->id)->first();
            if (empty($paymentTransaction)) {
                ResponseService::errorResponse("No Data Found");
            }
            $data = PaymentService::create($paymentTransaction->payment_gateway, $paymentTransaction->school_id)->retrievePaymentIntent($paymentTransaction->order_id);

            $data = PaymentService::formatPaymentIntent($paymentTransaction->payment_gateway, $data);

            // Success
            ResponseService::successResponse("Payment Details Fetched", $data, ['payment_transaction' => $paymentTransaction]);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getPaymentTransactions(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'latest_only' => 'nullable|boolean'
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            if (!Auth::user()->hasRole('School Admin') || Auth::user()->hasRole('Super Admin')) {
                $user_id = Auth::user()->id;
            }
            $paymentTransactions = app(PaymentTransactionInterface::class)->builder();
            if ($request->latest_only) {
                $paymentTransactions->where('created_at', '>', Carbon::now()->subMinutes(30)->toDateTimeString());
            }
            $paymentTransactions = $paymentTransactions->with('school')->orderBy('id', 'DESC');
            if (isset($user_id)) {
                if (Auth::user()->hasRole('Guardian')) {
                    $childIds = Students::where('guardian_id', $user_id)->pluck('user_id')->toArray();
                    $paymentTransactions->whereIn('user_id', $childIds);
                    $paymentTransactions->orWhere('user_id', $user_id);
                } else {
                    $paymentTransactions->where('user_id', $user_id);
                }
            }
            $paymentTransactions = $paymentTransactions->get();

            $schoolSettings = app(SchoolSettingInterface::class)->builder()
                ->where(function ($q) {
                    $q->where('name', 'currency_code')->orWhere('name', 'currency_symbol');
                })->whereIn('school_id', $paymentTransactions->pluck('school_id'))->get();

            $paymentTransactions = $paymentTransactions->map(function ($data) use ($schoolSettings) {
                $getSchoolSettings = $schoolSettings->filter(function ($settings) use ($data) {
                    return $settings->school_id == $data->school_id;
                })->where('status', 1)->pluck('data', 'name');
                $data->currency_code = $getSchoolSettings['currency_code'] ?? '';
                $data->currency_symbol = $getSchoolSettings['currency_symbol'] ?? '';
                if ($data->payment_status == "pending") {
                    try {
                        if ($data->order_id) {
                            // For Flutterwave, use tx_ref for verification
                            if ($data->payment_gateway == "Flutterwave") {
                                $paymentIntent = PaymentService::create($data->payment_gateway, $data->school_id)
                                    ->retrievePaymentIntent($data->order_id);
                                $paymentIntent = PaymentService::formatPaymentIntent($data->payment_gateway, $paymentIntent);

                                // Update transaction status based on verification
                                if (isset($paymentIntent['status'])) {
                                    $status = match (strtolower($paymentIntent['status'])) {
                                        'successful', 'completed', 'success' => 'succeed',
                                        'failed', 'cancelled' => 'failed',
                                        default => 'pending'
                                    };

                                    if ($status !== 'pending') {
                                        $this->paymentTransaction->update($data->id, [
                                            'payment_status' => $status,
                                            'payment_id' => $paymentIntent['transaction_id'] ?? null,
                                            'school_id' => $data->school_id
                                        ]);
                                        $data->payment_status = $status;
                                        if ($status === 'succeed') {
                                            $fee_id = Transportationpayment::where('payment_transaction_id', $data->id)->first();
                                            $transportationFee = TransportationFee::where('id', $fee_id->transportation_fee_id)->first();
                                            $expiryDate = null;
                                            if ($transportationFee) {
                                                if (!empty($transportationFee->duration)) {
                                                    $expiryDate = now()->addDays($transportationFee->duration);
                                                }
                                            }
                                            TransportationPayment::where('payment_transaction_id', $data->id)
                                                ->update([
                                                    'status' => "paid",
                                                    'paid_at' => Carbon::now()->format('Y-m-d H:i:s'),
                                                    'expiry_date' => $expiryDate
                                                ]);
                                        }
                                    }
                                }
                            } else {
                                // For other payment gateways
                                $paymentIntent = PaymentService::create($data->payment_gateway, $data->school_id)
                                    ->retrievePaymentIntent($data->order_id);
                                $paymentIntent = PaymentService::formatPaymentIntent($data->payment_gateway, $paymentIntent);

                                if ($paymentIntent['status'] != "pending") {
                                    $this->paymentTransaction->update($data->id, [
                                        'payment_status' => $paymentIntent['status'],
                                        'school_id' => $data->school_id
                                    ]);
                                    if ($paymentIntent['status'] == "succeed") {
                                        $transportationFee = TransportationFee::where('id', $paymentIntent['metadata']['fees_id'])->first();
                                        $expiryDate = null;
                                        if ($transportationFee) {
                                            if (!empty($transportationFee->duration)) {
                                                $expiryDate = now()->addDays($transportationFee->duration);
                                            }
                                        }
                                        TransportationPayment::where('payment_transaction_id', $data->id)
                                            ->update([
                                                'status' => "paid",
                                                'paid_at' => Carbon::now()->format('Y-m-d H:i:s'),
                                                'expiry_date' => $expiryDate
                                            ]);
                                    } else {
                                        TransportationPayment::where('payment_transaction_id', $data->id)
                                            ->update([
                                                'status' => "canceled"
                                            ]);
                                    }
                                    $data->payment_status = $paymentIntent['status'];
                                }
                            }
                        }
                    } catch (Exception $e) {
                        Log::error('Payment verification error:', [
                            'payment_id' => $data->id,
                            'order_id' => $data->order_id,
                            'error' => $e->getMessage()
                        ]);
                        // Don't update status on verification error
                    }
                }
                return $data;
            });

            ResponseService::successResponse("Payment Transactions Fetched", $paymentTransactions);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getGallery(Request $request)
    {
        try {
            $limit = $request->limit ?? 10;   // default limit
            $offset = $request->offset ?? 0;   // default offset 0

            if ($request->gallery_id) {
                $data = $this->gallery->builder()
                    ->with('file')
                    ->where('id', $request->gallery_id)
                    ->first();

                // Organize files into videos and photos with limit/offset
                if ($data) {
                    $data = $this->organizeGalleryFiles($data, $limit, $offset);
                }
            } else {
                if ($request->child_id) {
                    // $child = $this->student->findById($request->child_id);
                    $query = $this->gallery->builder()
                        ->with('file');
                    // ->where('school_id', $child->user->school_id);
                    if ($request->session_year_id) {
                        $query = $query->where('session_year_id', $request->session_year_id);
                    }
                    $data = $query->offset($offset)->limit($limit)->get();

                    // Organize files for each gallery with limit/offset
                    $data = $data->map(function ($gallery) use ($limit, $offset) {
                        return $this->organizeGalleryFiles($gallery, $limit, $offset);
                    });
                } else {
                    if ($request->session_year_id) {
                        $query = $this->gallery->builder()
                            ->with('file')
                            ->where('session_year_id', $request->session_year_id);
                    } else {
                        $query = $this->gallery->builder()
                            ->with('file');
                    }
                    $data = $query->offset($offset)->limit($limit)->get();

                    // Count total photos and videos for each gallery
                    $data = $data->map(function ($gallery) {
                        $total_photos = 0;
                        $total_videos = 0;

                        if ($gallery->file && $gallery->file->count() > 0) {
                            foreach ($gallery->file as $file) {
                                if ($file->type_detail == 'Youtube Link') {
                                    $total_videos++;
                                } elseif ($file->type_detail == 'File Upload') {
                                    $total_photos++;
                                }
                            }
                        }

                        // Add count params to the gallery object (as attributes for response)
                        $gallery->total_photos = $total_photos;
                        $gallery->total_videos = $total_videos;

                        return $gallery;
                    });
                    // Organize files for each gallery with limit/offset
                    $data = $data->map(function ($gallery) use ($limit, $offset) {
                        return $this->organizeGalleryFiles($gallery, $limit, $offset);
                    });
                }
            }
            ResponseService::successResponse("Gallery Fetched Successfully", $data);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    /**
     * Organize gallery files into videos and photos arrays based on type_detail
     * Apply limit and offset separately to each array
     */
    private function organizeGalleryFiles($gallery, $limit = 10, $offset = 0)
    {
        $videos = [];
        $photos = [];

        if ($gallery->file && $gallery->file->count() > 0) {
            foreach ($gallery->file as $file) {
                $fileArray = $file->toArray();
                if ($file->type_detail == 'Youtube Link') {
                    $videos[] = $fileArray;
                } elseif ($file->type_detail == 'File Upload') {
                    $photos[] = $fileArray;
                }
            }
        }

        // Apply limit and offset to videos array
        $videos = array_slice($videos, $offset, $limit);

        // Apply limit and offset to photos array
        $photos = array_slice($photos, $offset, $limit);

        // Convert to array if it's a model instance
        $galleryArray = $gallery->toArray();
        $galleryArray['videos'] = $videos;
        $galleryArray['photos'] = $photos;

        // Remove the original file relation
        unset($galleryArray['file']);

        return $galleryArray;
    }

    public function getSessionYear(Request $request)
    {
        try {
            if ($request->filled('child_id')) {
                $child = $this->student->findById($request->child_id);

                if (!$child) {
                    return ResponseService::validationError('Invalid child selected.');
                }

                $sessionYears = $this->sessionYear
                    ->builder()
                    ->where('school_id', $child->user->school_id)
                    ->get();
            } else {
                $sessionYears = $this->sessionYear->builder()->get();
            }

            $data = $sessionYears->map(function ($item) {
                return [
                    "id" => $item->id,
                    "name" => $item->name,
                    "default" => $item->default,
                    "start_date" => Carbon::parse($item->original_start_date)->format('d-m-Y'),
                    "end_date" => Carbon::parse($item->original_end_date)->format('d-m-Y'),
                    "school_id" => $item->school_id,
                    "created_at" => $item->created_at,
                    "updated_at" => $item->updated_at,
                    "deleted_at" => $item->deleted_at,
                ];
            });

            return ResponseService::successResponse(
                "Session Year Fetched Successfully",
                $data
            );
        } catch (\Throwable $e) {
            ResponseService::logErrorResponse($e);
            return ResponseService::errorResponse();
        }
    }

    public function getLeaves(Request $request)
    {
        ResponseService::noFeatureThenSendJson('Staff Leave Management');
        try {
            $leave = $this->leaveDetail->builder()->with('leave:id,user_id', 'leave.user:id,first_name,last_name,image', 'leave.user.roles')
                ->whereHas('leave', function ($q) {
                    $q->where('status', 1);
                });
            if ($request->type == 0 || $request->type == null) {
                $leave->whereDate('date', '<=', Carbon::now()->format('Y-m-d'))->whereDate('date', '>=', Carbon::now()->format('Y-m-d'));
            }
            if ($request->type == 1) {
                $tomorrow_date = Carbon::now()->addDay()->format('Y-m-d');
                $leave->whereDate('date', '<=', $tomorrow_date)->whereDate('date', '>=', $tomorrow_date);
            }
            if ($request->type == 2) {
                $upcoming_date = Carbon::now()->addDays(1)->format('Y-m-d');
                $leave->whereDate('date', '>', $upcoming_date);
            }
            $leave = $leave->orderBy('date', 'ASC')->get()->append(['leave_date']);
            ResponseService::successResponse("Data Fetched Successfully", $leave);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function applyLeaves(Request $request)
    {
        $file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
        $validator = Validator::make($request->all(), [
            'reason' => 'required',
            'files' => 'nullable|array',
            'files.*' => ['nullable', 'mimes:jpg,jpeg,png,pdf,doc,docx', new MaxFileSize($file_upload_size_limit)]
        ], [
            'files.*.max_file_size' => trans('The file Uploaded must be less than :file_upload_size_limit MB.', [
                'file_upload_size_limit' => $file_upload_size_limit,
            ]),
            'files.*.mimes' => 'Only JPG, JPEG, PNG, PDF, DOC, and DOCX files are allowed.',
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            DB::beginTransaction();
            $sessionYear = $this->cache->getDefaultSessionYear();
            $leaveMaster = $this->leaveMaster->builder()->where('session_year_id', $sessionYear->id)->first();

            if (!$leaveMaster) {
                ResponseService::errorResponse("Kindly contact the school admin to update settings for continued access.");
            }

            $public_holiday = $this->holiday->builder()->whereDate('date', '>=', $sessionYear->start_date)->whereDate('date', '<=', $sessionYear->end_date)->get()->pluck('dmyFormat')->toArray();

            $dates = collect($request->leave_details)
                ->pluck('date')
                ->map(fn($d) => \Carbon\Carbon::parse($d));
            if ($dates->isEmpty()) {
                ResponseService::validationError('Leave dates cannot be empty.');
            }
            $from_date = $dates->min()->toDateString();
            $to_date = $dates->max()->toDateString();

            $exists = $this->leave->builder()
                ->where('user_id', Auth::user()->id)
                ->where(function ($q) use ($from_date, $to_date) {
                    $q->where('from_date', '<=', $to_date)
                        ->where('to_date', '>=', $from_date);
                })
                ->exists();

            if ($exists) {
                ResponseService::errorResponse('You already have a leave request during this period.');
            }

            $leave_data = [
                'user_id' => Auth::user()->id,
                'reason' => $request->reason,
                'from_date' => $from_date,
                'to_date' => $to_date,
                'status' => 0,
                'leave_master_id' => $leaveMaster->id
            ];

            $holidays = explode(',', $leaveMaster->holiday);

            $leave = $this->leave->create($leave_data);
            foreach ($request->leave_details as $key => $leaves) {
                $date = date('d-m-Y', strtotime($leaves['date']));
                $day = date('l', strtotime($leaves['date']));
                if (!in_array($day, $holidays) && !in_array($date, $public_holiday)) {
                    $data[] = [
                        'leave_id' => $leave->id,
                        'date' => $leaves['date'],
                        'type' => $leaves['type']
                    ];
                } else {
                    ResponseService::errorResponse("Kindly select different dates as the ones mentioned are already allocated as holidays.");
                }
            }
            if ($request->hasFile('files')) {
                $fileData = []; // Empty FileData Array
                // Create A File Model Instance
                $leaveModelAssociate = $this->files->model()->modal()->associate($leave);
                foreach ($request->file('files') as $file_upload) {
                    // Create Temp File Data Array
                    $tempFileData = [
                        'modal_type' => $leaveModelAssociate->modal_type,
                        'modal_id' => $leaveModelAssociate->modal_id,
                        'file_name' => $file_upload->getClientOriginalName(),
                        'type' => 1,
                        'file_url' => $file_upload->store('files', 'public') // Store file and get the file path
                    ];
                    $fileData[] = $tempFileData; // Store Temp File Data in Multi-Dimensional File Data Array
                }
                $this->files->createBulk($fileData); // Store File Data
            }

            $this->leaveDetail->createBulk($data);

            $user = $this->user->builder()->whereHas('roles.permissions', function ($q) {
                $q->where('name', 'approve-leave');
            })->pluck('id')->toArray();

            $type = "Leave";
            $title = Auth::user()->full_name . ' has submitted a new leave request.';
            $body = $request->reason;

            DB::commit();

            send_notification($user, $title, $body, $type);

            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 {
                ResponseService::logErrorResponse($e);
                ResponseService::errorResponse();
            }
        }
    }

    public function getMyLeaves(Request $request)
    {
        ResponseService::noFeatureThenSendJson('Staff Leave Management');
        $validator = Validator::make($request->all(), [
            'month' => 'in:1,2,3,4,5,6,7,8,9,10,11,12',
            'status' => 'in:0,1,2'
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            $sessionYear = $this->cache->getDefaultSessionYear();
            $leaveMaster = $this->leaveMaster->builder();
            $sql = $this->leave->builder()->with('leave_detail')->where('user_id', Auth::user()->id)->withCount([
                'leave_detail as full_leave' => function ($q) {
                    $q->where('type', 'Full');
                }
            ])->withCount([
                'leave_detail as half_leave' => function ($q) {
                    $q->whereNot('type', 'Full');
                }
            ]);

            if ($request->session_year_id) {
                $sql->whereHas('leave_master', function ($q) use ($request) {
                    $q->where('session_year_id', $request->session_year_id);
                });
                $leaveMaster->where('session_year_id', $request->session_year_id);
            } else {
                $sql->whereHas('leave_master', function ($q) use ($sessionYear) {
                    $q->where('session_year_id', $sessionYear->id);
                });
                $leaveMaster->where('session_year_id', $sessionYear->id);
            }
            if (isset($request->status)) {
                $sql->where('status', $request->status);
            }
            if ($request->month) {
                $sql->whereHas('leave_detail', function ($q) use ($request) {
                    $q->whereMonth('date', $request->month);
                });
            }
            $leaveMaster = $leaveMaster->first();
            $sql = $sql->get();
            $sql = $sql->map(function ($sql) {
                $total_leaves = ($sql->half_leave / 2) + $sql->full_leave;
                $sql->days = $total_leaves;
                return $sql;
            });
            $data = [
                'monthly_allowed_leaves' => $leaveMaster->leaves,
                'taken_leaves' => $sql->sum('days'),
                'leave_details' => $sql
            ];

            ResponseService::successResponse("Data Fetched Successfully", $data);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function deleteLeaves(Request $request)
    {
        ResponseService::noFeatureThenSendJson('Staff Leave Management');
        $validator = Validator::make($request->all(), [
            'leave_id' => 'required',
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            DB::beginTransaction();
            $leave = $this->leave->findById($request->leave_id);
            if ($leave->status != 0) {
                ResponseService::errorResponse("You cannot delete this leave");
            }
            $this->leave->deleteById($request->leave_id);
            DB::commit();
            ResponseService::successResponse("Data Deleted Successfully");
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getStaffLeaveDetail(Request $request)
    {
        ResponseService::noFeatureThenSendJson('Staff Leave Management');
        $validator = Validator::make($request->all(), [
            'staff_id' => 'required',
            'month' => 'in:1,2,3,4,5,6,7,8,9,10,11,12',
            'status' => 'in:0,1,2'
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            $sessionYear = $this->cache->getDefaultSessionYear();
            $leaveMaster = $this->leaveMaster->builder();
            $sql = $this->leave->builder()->with('leave_detail', 'file')->withCount([
                'leave_detail as full_leave' => function ($q) {
                    $q->where('type', 'Full');
                }
            ])->withCount([
                'leave_detail as half_leave' => function ($q) {
                    $q->whereNot('type', 'Full');
                }
            ])->where('user_id', $request->staff_id);

            if ($request->session_year_id) {
                $sql->whereHas('leave_master', function ($q) use ($request) {
                    $q->where('session_year_id', $request->session_year_id);
                });
                $leaveMaster->where('session_year_id', $request->session_year_id);
            } else {
                $sql->whereHas('leave_master', function ($q) use ($sessionYear) {
                    $q->where('session_year_id', $sessionYear->id);
                });
                $leaveMaster->where('session_year_id', $sessionYear->id);
            }
            if (isset($request->status)) {
                $sql->where('status', $request->status);
            }
            if ($request->month) {
                $sql->whereHas('leave_detail', function ($q) use ($request) {
                    $q->whereMonth('date', $request->month);
                });
            }
            $leaveMaster = $leaveMaster->first();
            if (!$leaveMaster) {
                ResponseService::errorResponse("Leave settings not found");
            }
            $sql = $sql->get();
            $sql = $sql->map(function ($sql) {
                $total_leaves = ($sql->half_leave / 2) + $sql->full_leave;
                $sql->days = $total_leaves;
                if ($sql->status == 1) {
                    $sql->taken_leaves = $total_leaves;
                }
                return $sql;
            });
            $data = [
                'monthly_allowed_leaves' => $leaveMaster->leaves,
                'total_leaves' => $sql->sum('days'),
                'taken_leaves' => $sql->sum('taken_leaves'),
                'leave_details' => $sql
            ];

            ResponseService::successResponse("Data Fetched Successfully", $data);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getMedium()
    {
        try {
            $sql = $this->medium->builder()->get();
            ResponseService::successResponse("Data Fetched Successfully", $sql);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getClass(Request $request)
    {
        try {
            $currentSemester = $this->cache->getDefaultSemesterData();
            $defaultSessionYear = $this->cache->getDefaultSessionYear();

            // Teacher
            if (Auth::user()->hasRole('Teacher')) {
                $user = $request->user()->teacher;
                //Find the class in which teacher is assigns as Class Teacher
                $class_section_ids = [];
                if ($user->class_teacher) {
                    // $class_teacher = $user->class_teacher;
                    // $class_teacher = $class_teacher->load('class_section.class.stream', 'class_section.class.shift', 'class_section.section', 'class_section.medium');
                    $class_teacher = $user->class_teacher()
                        ->where('session_year_id', $defaultSessionYear->id)
                        ->with([
                            'class_section.class.stream',
                            'class_section.class.shift',
                            'class_section.section',
                            'class_section.medium'
                        ])
                        ->get();
                    $class_section_ids = $class_teacher->pluck('class_section_id');
                }
                //Find the Classes in which teacher is taking subjects
                $class_section = $this->classSection->builder()->with('class.stream', 'class.shift', 'section', 'medium')->whereNotIn('id', $class_section_ids);

                $class_section = $class_section->whereHas('subject_teachers.class_subject', function ($q) use ($currentSemester) {
                    (!empty($currentSemester)) ? $q->where('semester_id', $currentSemester->id)->orWhereNull('semester_id') : $q->orWhereNull('semester_id');
                })->get();
            } else {
                // Staff
                $class_section = $this->classSection->builder()->with('class', 'class.shift', 'section', 'medium', 'class.stream')->get();
            }
            ResponseService::successResponse('Data Fetched Successfully', null, [
                'class_teacher' => $class_teacher ?? [],
                'other' => $class_section,
                'semester' => $currentSemester ?? null
            ]);
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }

    public function updateProfile(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'first_name' => 'required',
            'last_name' => 'required',
            'mobile' => 'required',
            'email' => 'required|unique:users,email,' . Auth::user()->id,
            'dob' => 'required',
            'current_address' => 'required',
            'permanent_address' => 'required',
            'gender' => 'required|in:male,female',
            'image' => 'nullable|image|mimes:jpeg,png,jpg,svg|max:5120',
        ]);
        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        if (Auth::user()->school_id) {
            $schoolSettings = $this->cache->getSchoolSettings();
        } else {
            $schoolSettings = $this->cache->getSystemSettings();
        }
        try {
            $user_data = [
                'first_name' => $request->first_name,
                'last_name' => $request->last_name,
                'mobile' => $request->mobile,
                'email' => $request->email,
                'dob' => Carbon::parse($request->dob)->format('Y-m-d'),
                'current_address' => $request->current_address,
                'permanent_address' => $request->permanent_address,
                'gender' => $request->gender,
            ];

            if ($request->hasFile('image')) {
                $user_data['image'] = $request->file('image');
            }

            $user = $this->user->update(Auth::user()->id, $user_data);

            $extraDetails = [];
            foreach ($request->custom_fields ?? [] as $fields) {
                if ($fields['input_type'] == 'file' && $fields['data'] instanceof UploadedFile) {
                    if (isset($fields['data'])) {
                        $extraDetails[] = array(
                            'id' => $fields['id'],
                            'user_id' => $user->id,
                            'form_field_id' => $fields['form_field_id'],
                            'data' => $fields['data']
                        );
                    }
                } else {
                    $data = null;
                    if (isset($fields['data'])) {
                        $data = (is_array($fields['data']) ? json_encode($fields['data'], JSON_THROW_ON_ERROR) : $fields['data']);
                    }
                    $extraDetails[] = array(
                        'id' => $fields['id'],
                        'user_id' => $user->id,
                        'form_field_id' => $fields['form_field_id'],
                        'data' => $data,
                    );
                }
            }
            $this->extraFormFields->upsert($extraDetails, ['id'], ['data']);

            $user = User::where('id', Auth::id())
                ->with(['extra_user_details.form_field'])
                ->first();

            $customFields = $user->extra_user_details->map(function ($row) {
                $field = $row->form_field;
                $value = $row->data;
                if ($field->type === 'file' && !empty($value)) {
                    $raw = $row->getRawOriginal('data');

                    $value = url('storage/' . ltrim($raw, '/'));
                }
                return [
                    'id' => $row->id,
                    'form_field_id' => $field->id,
                    'name' => $field->name,
                    'type' => $field->type,
                    'is_required' => (bool) $field->is_required,
                    'default_value' => $field->default_values,
                    'value' => $value,
                    'user_type' => $field->user_type == 1 ? 'student' : 'teacher/staff',
                    'rank' => $field->rank,
                ];
            });

            $user->custom_fields = $customFields;
            unset($user->extra_user_details);

            ResponseService::successResponse('Data Updated Successfully', $user);
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }

    public function getExamResultPdf(Request $request)
    {
        ResponseService::noFeatureThenSendJson('Exam Management');


        try {

            $validator = Validator::make($request->all(), [
                'exam_id' => 'required',
                'student_id' => 'required',
            ]);
            if ($validator->fails()) {
                ResponseService::validationError($validator->errors()->first());
            }

            $exam_id = $request->exam_id;
            $student_id = $request->student_id;

            $results = $this->examResult->builder()
                ->with([
                    'exam',
                    'session_year',
                    'class_section.class.stream',
                    'class_section.class.shift',
                    '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'))->output();

            return $response = array(
                'error' => false,
                'pdf' => base64_encode($pdf),
            );
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function leaveSettings(Request $request)
    {
        try {

            if ($request->session_year_id) {
                $session_year_id = $request->session_year_id;
            } else {
                $sessionYear = $this->cache->getDefaultSessionYear();
                $session_year_id = $sessionYear->id;
            }
            $sql = $this->leaveMaster->builder()->where('session_year_id', $session_year_id)->get();
            $public_holiday = $this->holiday->builder()->whereDate('date', '>=', $sessionYear->start_date)->whereDate('date', '<=', $sessionYear->end_date)->get(['date', 'title']);
            ResponseService::successResponse("Data Fetched Successfully", ['leave_settings' => $sql, 'public_holidays' => $public_holiday]);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getSchoolSettings(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'type' => 'required|in:privacy_policy,terms_condition,refund_cancellation,about_us,contact_us'
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {

            if ($request->child_id) {
                $child = $this->student->builder()->where('id', $request->child_id)->first();
                if (!$child) {
                    ResponseService::errorResponse("no_data_found");
                }
                $schoolSettings = $this->cache->getSchoolSettings('*', $child->school_id);
            } else {
                $schoolSettings = $this->cache->getSchoolSettings();
            }

            $data = isset($schoolSettings[$request->type]) ? htmlspecialchars_decode($schoolSettings[$request->type]) : "";
            ResponseService::successResponse("Data Fetched Successfully", $data);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function sendMessage(Request $request)
    {

        $validator = Validator::make($request->all(), [
            'to' => 'required'
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        $message = '';

        try {
            DB::beginTransaction();
            $chat = $this->chat->builder()
                ->where(function ($q) use ($request) {
                    $q->where('sender_id', Auth::user()->id)->where('receiver_id', $request->to);
                })
                ->orWhere(function ($q) use ($request) {
                    $q->where('sender_id', $request->to)->where('receiver_id', Auth::user()->id);
                })
                ->first();

            if (!$chat) {
                $chat = $this->chat->create(['sender_id' => Auth::user()->id, 'receiver_id' => $request->to]);
            }

            $chat = $chat->load(['receiver']);

            $message = $this->message->create(['chat_id' => $chat->id, 'sender_id' => Auth::user()->id, 'message' => $request->message]);
            // $message = new Message();
            // $message->chat_id = $chat->id;
            // $message->sender_id = Auth::user()->id;
            // $message->message = $request->message;
            // $message->save();

            $message = $message->refresh();

            $data = [];
            $subjectName = '';
            // Prepare customData if Teacher is messaging Student
            $customData = [];
            $receiverUser = User::find($request->to);
            if (Auth::user()->hasRole('Teacher') && $receiverUser && $receiverUser->hasRole('Student')) {
                // Get student's class_section_id
                $student = $receiverUser->student;

                if ($student && $student->class_section_id) {
                    // Get teacher's subject teachers for the student's class section
                    $teacherSubjectTeachers = SubjectTeacher::where('teacher_id', Auth::user()->id)
                        ->where('class_section_id', $student->class_section_id)
                        ->with('class_subject.subject')
                        ->get();

                    // Get student's subjects (student_id in StudentSubject references Students.user_id)
                    $studentSubjects = \App\Models\StudentSubject::where('student_id', $student->user_id)
                        ->where('class_section_id', $student->class_section_id)
                        ->pluck('class_subject_id')
                        ->toArray();

                    // Find common subject
                    foreach ($teacherSubjectTeachers as $subjectTeacher) {
                        if (in_array($subjectTeacher->class_subject_id, $studentSubjects)) {
                            if ($subjectTeacher->class_subject && $subjectTeacher->class_subject->subject) {
                                $subjectName = $subjectTeacher->class_subject->subject->name;
                                if ($subjectTeacher->class_subject->subject->type) {
                                    $subjectName .= ' (' . $subjectTeacher->class_subject->subject->type . ')';
                                }
                            }
                            break; // Get first matching subject
                        }
                    }
                }
            }

            $customData = [
                'receiver_id' => Auth::user()->id,
                'teacher_name' => Auth::user()->full_name ?? '',
                'teacher_image' => Auth::user()->image ?? '',
                'subject_name' => $subjectName,
            ];

            if ($request->hasFile('files')) {
                foreach ($request->file('files') as $file) {
                    $file_path = $file->store('chat_file', 'public');
                    $data[] = [
                        'message_id' => $message->id,
                        'file' => $file_path,
                        'file_type' => $file->getClientOriginalExtension()
                    ];
                }
                $this->attachment->createBulk($data);

                // set attachment
                $message->load(['attachment']);
                $messageData = $message->toArray();
                $messageData['created_at'] = $message->created_at;
                $messageData['updated_at'] = $message->updated_at;

                // send notification
                $user[] = $request->to;
                $title = 'New Message from ' . Auth::user()->full_name;

                $fileNames = array_map(function ($file) {
                    return basename($file->getClientOriginalName());
                }, $request->file('files'));

                $body = $request->message ? $request->message . ' (Files: ' . implode(', ', $fileNames) . ')'
                    : 'Files attached: ' . implode(', ', $fileNames);
                $type = 'Message';

                DB::commit();

                broadcast(new MessageSent(
                    channel: 'user.' . $request->to,
                    event: 'NewMessage',
                    message: $messageData,
                    from: Auth::id(),
                    to: $request->to,
                ));

                send_notification($user, $title, $body, $type, $customData);

                ResponseService::successResponse("Data Stored Successfully", $messageData);
            } else {
                // Only send notification if no files attached
                $user[] = $request->to;
                $title = 'New Message from ' . Auth::user()->full_name;
                $body = $request->message ?? 'No message';
                $type = 'Message';

                $messageData = $message->toArray();
                $messageData['created_at'] = $message->created_at;
                $messageData['updated_at'] = $message->updated_at;

                DB::commit();

                broadcast(new MessageSent(
                    channel: 'user.' . $request->to,
                    event: 'NewMessage',
                    message: $messageData,
                    from: Auth::id(),
                    to: (int) $request->to,
                ));

                send_notification($user, $title, $body, $type, $customData);

                ResponseService::successResponse("Data Stored Successfully", $messageData);
            }
        } catch (\Throwable $th) {
            $notificationStatus = app(GeneralFunctionService::class)->wrongNotificationSetup($th);
            if ($notificationStatus) {
                DB::rollBack();
                ResponseService::logErrorResponse($th);
                ResponseService::errorResponse();
            } else {
                DB::commit();
                if (is_object($message)) {
                    $messageData = $message->toArray();
                    $messageData['created_at'] = $message->created_at;
                    $messageData['updated_at'] = $message->updated_at;
                } else {
                    $messageData = $message;
                }
                ResponseService::warningResponse("Data Stored successfully. But App push notification not send.", $messageData);
            }
        }
    }

    public function getMessage(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'receiver_id' => 'required'
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }

        try {
            $data = [];
            $data = $this->message->builder()->whereHas('chat', function ($q) use ($request) {
                $q->where(function ($q) use ($request) {
                    $q->where('sender_id', Auth::user()->id)->where('receiver_id', $request->receiver_id);
                })
                    ->orWhere(function ($q) use ($request) {
                        $q->where('sender_id', $request->receiver_id)->where('receiver_id', Auth::user()->id);
                    });
            })
                ->with('attachment')->orderBy('id', 'DESC')
                ->paginate(10);

            ResponseService::successResponse("data_fetch_successfully", $data);
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }

    public function deleteMessage(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'id' => 'required'
        ]);
        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            $this->message->builder()->whereIn('id', $request->id)->delete();
            ResponseService::successResponse("Data Deleted Successfully");
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }

    public function readMessage(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'message_id' => 'required'
        ]);
        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            $read_at = Carbon::now();
            $this->message->builder()->whereIn('id', $request->message_id)->update(['read_at' => $read_at]);
            ResponseService::successResponse("Data Updated Successfully");
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }

    public function getUsers(Request $request)
    {
        if (Auth::user()->hasRole('Teacher')) {
            $validator = Validator::make($request->all(), [
                'role' => 'required|in:Guardian,Staff,Student,Teacher',
            ]);
            if ($validator->fails()) {
                ResponseService::validationError($validator->errors()->first());
            }
        }

        try {

            $users = [];
            $currentSemester = $this->cache->getDefaultSemesterData(Auth::user()->school_id);
            $sessionYear = $this->cache->getDefaultSessionYear();
            $sessionYearId = $sessionYear->id;
            if (Auth::user()) {
                $searchTerm = $request->search;
                DB::enableQueryLog();
                $users = User::select('id', 'first_name', 'last_name', 'image', 'school_id')->whereNot('id', Auth::user()->id)->where('status', 1);
                if ($request->search) {
                    $search = $request->search;
                    $users->where(function ($query) use ($search) {
                        $query->where('first_name', 'LIKE', "%$search%")
                            ->orWhere('last_name', 'LIKE', "%$search%");
                    });
                }

                if (Auth::user()->hasRole('Student')) {

                    $userId = Auth::user()->id;
                    $student = Auth::user()->student;
                    $classId = $student->class_section->class_id;

                    $class_subject_ids = ClassSubject::where('class_id', $classId)
                        ->where(function ($q) use ($currentSemester) {
                            if ($currentSemester) {
                                $q->where('semester_id', $currentSemester->id)
                                    ->orWhereNull('semester_id');
                            } else {
                                $q->whereNull('semester_id');
                            }
                        })
                        ->pluck('id')
                        ->toArray();

                    $class_teachers = ClassTeacher::where('class_section_id', $student->class_section_id)
                        ->pluck('teacher_id')
                        ->toArray();

                    // Transportation (optional)
                    $allowedUserIds = [];
                    $transportationPayment = TransportationPayment::where('user_id', $userId)
                        ->where('expiry_date', '>=', now())
                        ->whereNotNull('route_vehicle_id')
                        ->first();

                    if ($transportationPayment) {
                        $routeVehicle = RouteVehicle::find($transportationPayment->route_vehicle_id);
                        $allowedUserIds = array_filter([
                            $routeVehicle?->driver_id,
                            $routeVehicle?->helper_id,
                        ]);
                    }

                    $users = $users
                        ->with([
                            'subjectTeachers' => function ($q) use ($class_teachers, $class_subject_ids) {
                                $q->whereIn('class_subject_id', $class_subject_ids)
                                    ->whereNotIn('teacher_id', $class_teachers)
                                    ->with('subject:id,name');
                            },
                            'class_teacher.class_section.class.stream',
                            'class_teacher.class_section.class.shift',
                            'class_teacher.class_section.section',
                            'class_teacher.class_section.medium',
                            'roles'
                        ])
                        ->where(function ($query) use ($class_subject_ids, $class_teachers, $allowedUserIds, $searchTerm, $sessionYearId) {

                            // 🎓 Academic teachers
                            $query->whereHas('subjectTeachers', function ($q) use ($class_subject_ids, $class_teachers) {
                                $q->whereIn('class_subject_id', $class_subject_ids)
                                    ->whereNotIn('teacher_id', $class_teachers);
                            })
                                ->orWhereHas('class_teacher', function ($q) use ($class_teachers) {
                                    $q->whereIn('teacher_id', $class_teachers);
                                });

                            // 🚌 Transportation staff
                            if (!empty($allowedUserIds)) {
                                $query->orWhereIn('id', $allowedUserIds);
                            }

                            $query->orWhereHas('roles', function ($q) {
                                $q->where('custom_role', 1);
                            });

                            $query->whereHas('staff', function ($q) use ($sessionYearId) {
                                // $q->where('join_session_year_id', $sessionYearId);
                            });

                            $query->WhereHas('roles', function ($r) {
                                $r->whereNot('name', 'School Admin');
                            });
                        })
                        ->where(function ($q) use ($searchTerm) {
                            if ($searchTerm) {
                                $q->where('first_name', 'like', "%{$searchTerm}%")
                                    ->orWhere('last_name', 'like', "%{$searchTerm}%");
                            }
                        });
                } else if (Auth::user()->hasRole('Teacher')) { // Teacher login
                    // Get guardian list
                    if ($request->role == 'Guardian') {
                        $validator = Validator::make($request->all(), [
                            'class_section_id' => 'required',
                        ]);
                        if ($validator->fails()) {
                            ResponseService::validationError($validator->errors()->first());
                        }
                        $users = $users->role(['Guardian'])->whereHas('child', function ($q) use ($request, $sessionYearId) {
                            $q->where('school_id', Auth::user()->school_id)
                                ->where('session_year_id', $sessionYearId)
                                ->where('class_section_id', $request->class_section_id);
                        })->with('child:id,user_id,guardian_id,class_section_id', 'child.user:id,first_name,last_name,image');
                    } else if ($request->role == 'Staff') {

                        $userId = Auth::user()->id;
                        $allowedUserIds = [];
                        if ($userId) {
                            $transportationPayment = TransportationPayment::where('user_id', $userId)
                                ->where('expiry_date', '>=', Carbon::now())
                                ->whereNotNull('route_vehicle_id')
                                ->first();

                            if ($transportationPayment) {
                                $routeVehicle = RouteVehicle::find($transportationPayment->route_vehicle_id);

                                // ✅ driver_id & helper_id are USER IDs
                                $allowedUserIds = array_filter([
                                    $routeVehicle?->driver_id,
                                    $routeVehicle?->helper_id,
                                ]);
                            }
                        }
                        // Get staff list

                        $users = $users
                            ->where('school_id', Auth::user()->school_id)
                            ->where(function ($q) use ($allowedUserIds, $sessionYearId) {

                                // (staff AND custom_role)
                                $q->where(function ($q2) use ($sessionYearId) {
                                    $q2->whereHas('staff', function ($q) use ($sessionYearId) {
                                        // $q->where('join_session_year_id', $sessionYearId);
                                    })
                                        ->orWhereHas('roles', function ($r) {
                                            $r->where('custom_role', 1);
                                        });
                                });

                                // OR School Admin
                                $q->orWhereHas('roles', function ($r) {
                                    $r->where('name', 'School Admin');
                                });

                                if (!empty($allowedUserIds)) {
                                    $q->orWhereIn('id', $allowedUserIds);
                                }
                            });
                    }
                } else if (Auth::user()->hasRole('Guardian')) { // Guardian login

                    $users = $users
                        ->where('school_id', Auth::user()->school_id)
                        ->where(function ($q) use ($sessionYearId) {

                            // Staff users
                            $q->whereHas('staff', function ($q) use ($sessionYearId) {
                                // $q->where('join_session_year_id', $sessionYearId);
                            });

                            // OR School Admins (even without staff record)
                            $q->orWhereHas('roles', function ($r) {
                                $r->where('name', 'School Admin');
                            });
                        });
                    $driverId = null;
                    $helperId = null;
                    if ($request->role === 'Staff') {

                        // Always initialize
                        $allowedUserIds = [];

                        if ($request->child_id) {

                            // ✅ FIX: get scalar, not collection
                            $userId = Students::where('id', $request->child_id)->where('session_year_id', $sessionYearId)->value('user_id');

                            if ($userId) {
                                $transportationPayment = TransportationPayment::where('user_id', $userId)
                                    ->where('expiry_date', '>=', Carbon::now())
                                    ->whereNotNull('route_vehicle_id')
                                    ->first();

                                if ($transportationPayment) {
                                    $routeVehicle = RouteVehicle::find($transportationPayment->route_vehicle_id);

                                    // ✅ driver_id & helper_id are USER IDs
                                    $allowedUserIds = array_filter([
                                        $routeVehicle?->driver_id,
                                        $routeVehicle?->helper_id,
                                    ]);
                                }
                            }
                        }

                        $users = $users
                            ->with('roles')
                            ->where(function ($query) use ($allowedUserIds) {

                                // Custom staff
                                $query->whereHas('roles', function ($q) {
                                    $q->where('custom_role', 1);
                                });

                                // OR School Admin
                                $query->orWhereHas('roles', function ($q) {
                                    $q->where('name', 'School Admin');
                                });

                                // OR assigned Driver / Helper
                                if (!empty($allowedUserIds)) {
                                    $query->orWhereIn('id', $allowedUserIds);
                                }
                            });
                    } else {
                        if ($request->child_id) {
                            $class_sections = Students::where('id', $request->child_id)->where('session_year_id', $sessionYearId)->pluck('class_section_id')->toArray();
                        } else {
                            $child_ids = Auth::user()->load('child.user')->child->pluck('id')->toArray();
                            $class_sections = Students::whereIn('id', $child_ids)->where('session_year_id', $sessionYearId)->pluck('class_section_id')->toArray();
                        }

                        $class_sections = array_filter($class_sections);

                        $class_teachers = ClassTeacher::whereIn('class_section_id', $class_sections)->pluck('teacher_id')->toArray();

                        $class_teachers = array_unique($class_teachers);
                        $class_teachers = array_values($class_teachers);

                        $subject_teachers = SubjectTeacher::whereIn('class_section_id', $class_sections)->pluck('teacher_id')->toArray();

                        $subject_teachers = array_unique($subject_teachers);
                        $subject_teachers = array_values($subject_teachers);

                        $teacher_ids = array_merge($class_teachers, $subject_teachers);
                        $teacher_ids = array_unique($teacher_ids);
                        $teacher_ids = array_values($teacher_ids);

                        $users = $users->whereIn('id', $teacher_ids)->with([
                            'subjectTeachers' => function ($q) use ($class_sections) {
                                $q->whereIn('class_section_id', $class_sections)
                                    ->with('subject:id,name');
                            }
                        ])
                            ->where(function ($query) use ($searchTerm) {
                                $query->where('first_name', 'like', '%' . $searchTerm . '%')
                                    ->orWhere('last_name', 'like', '%' . $searchTerm . '%');
                            })
                            ->with([
                                'class_teacher' => function ($q) use ($class_sections) {
                                    $q->whereIn('class_section_id', $class_sections)
                                        ->with('class_section.class.stream', 'class_section.class.shift', 'class_section.section', 'class_section.medium');
                                }
                            ]);
                        // ->whereHas('staff', function ($q) use ($sessionYearId) {
                        //     $q->where('join_session_year_id', $sessionYearId);
                        // });
                    }



                    // =====================
                    // $users = $users->whereHas('subjectTeachers', function($q) use($class_sections) {
                    //     $q->whereIn('class_section_id', $class_sections);
                    // })
                    // ->with(['subjectTeachers' => function($q) use($class_sections){
                    //     $q->whereIn('class_section_id', $class_sections)
                    //     ->with('subject:id,name');
                    // }])
                    // ->where(function($query) use ($searchTerm) {
                    //     $query->where('first_name', 'like', '%' . $searchTerm . '%')
                    //           ->orWhere('last_name', 'like', '%' . $searchTerm . '%');
                    // })
                    // ->orWhereHas('class_teacher',function($q) use($class_sections) {
                    //     $q->whereIn('class_section_id', $class_sections);
                    // })
                    // ->with(['class_teacher' => function($q) use($class_sections) {
                    //     $q->whereIn('class_section_id',$class_sections)
                    //     ->with('class_section.class.stream','class_section.section','class_section.medium');
                    // }])
                    // ->has('staff');


                } else if (Auth::user()->hasRole('School Admin')) { // Admin or Super Admin login
                    if ($request->role == 'Guardian') {
                        $validator = Validator::make($request->all(), [
                            'class_section_id' => 'required',
                        ]);
                        if ($validator->fails()) {
                            ResponseService::validationError($validator->errors()->first());
                        }
                        $users = $users->role(['Guardian'])->whereHas('child', function ($q) use ($request, $sessionYearId) {
                            $q->where('school_id', Auth::user()->school_id)
                                ->where('session_year_id', $sessionYearId)
                                ->where('class_section_id', $request->class_section_id);
                        })->with('child:id,user_id,guardian_id,class_section_id', 'child.user:id,first_name,last_name,image');
                    } else if ($request->role == 'Staff') {
                        // Get staff list

                        $users = $users
                            ->where('school_id', Auth::user()->school_id)
                            ->whereHas('staff', function ($q) use ($sessionYearId) {
                                // $q->where('join_session_year_id', $sessionYearId);
                            })
                            ->where(function ($q) {

                                // custom staff
                                $q->whereHas('roles', function ($r) {
                                    $r->where('custom_role', 1);
                                });

                                // OR Driver / Helper (custom_role = 0)
                                $q->orWhereHas('roles', function ($r) {
                                    $r->where('custom_role', 0)
                                        ->whereIn('name', ['Driver', 'Helper']);
                                });
                            });
                    }
                } else if (Auth::user()->hasRole('Staff') || Auth::user()->roles->first()->custom_role == 1 || Auth::user()->hasRole('Driver') || Auth::user()->hasRole('Helper')) { // Staff login
                    if ($request->role == 'Guardian') {
                        $validator = Validator::make($request->all(), [
                            'class_section_id' => 'required',
                        ]);
                        if ($validator->fails()) {
                            ResponseService::validationError($validator->errors()->first());
                        }
                        $users = $users->role(['Guardian'])->whereHas('child', function ($q) use ($request, $sessionYearId) {
                            $q->where('school_id', Auth::user()->school_id)
                                ->where('session_year_id', $sessionYearId)
                                ->where('class_section_id', $request->class_section_id);
                        })->with('child:id,user_id,guardian_id,class_section_id', 'child.user:id,first_name,last_name,image');
                    } else if ($request->role == 'Staff') {
                        // Get staff list

                        $users = $users->where('school_id', Auth::user()->school_id)
                            ->whereHas('staff', function ($q) use ($sessionYearId) {
                                // $q->where('join_session_year_id', $sessionYearId);
                            })
                            ->whereHas('roles', function ($q) {
                                $q->where('custom_role', 1);
                            })->orWhereHas('roles', function ($q) {
                                $q->where('name', 'School Admin');
                            });
                    } else if ($request->role == 'Student') {
                        $validator = Validator::make($request->all(), [
                            'class_section_id' => 'required',
                        ]);
                        if ($validator->fails()) {
                            ResponseService::validationError($validator->errors()->first());
                        }
                        $users = $users->role(['Student'])->whereHas('student', function ($q) use ($request, $sessionYearId) {
                            $q->where('school_id', Auth::user()->school_id)
                                ->where('session_year_id', $sessionYearId)
                                ->where('class_section_id', $request->class_section_id);
                        })->with('student:id,user_id,class_section_id', 'student.class_section:id,class_id,section_id,medium_id', 'student.class_section.class:id,name,shift_id', 'student.class_section.class.shift:id,name', 'student.class_section.section:id,name', 'student.class_section.medium:id,name');
                    }
                } else { // Staff login
                    $users = $users->where('school_id', Auth::user()->school_id);
                }
                if ($request->role == 'Staff') {
                    $users = $users->whereHas('roles', function ($q) use ($request) {
                        $q->whereNotIn('name', ['Student', 'Guardian']);
                    })->orderBy('first_name', 'ASC')->with('roles')->paginate(10);
                } else {
                    $users = $users->whereHas('roles', function ($q) use ($request) {
                        $q->where('name', $request->role);
                    })->orderBy('first_name', 'ASC')->with('roles')->paginate(10);
                }
            }

            ResponseService::successResponse("Data Fetched Successfully", $users);
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }

    public function getUsersByRoles(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'roles' => 'required',
        ]);

        if ($validator->fails()) {
            return ResponseService::validationError($validator->errors()->first());
        }

        try {
            $roles = is_array($request->roles)
                ? $request->roles
                : explode(',', $request->roles);
            $type = $request->type ?? null;
            $users = User::select('id', 'first_name', 'last_name', 'image')
                ->where('school_id', Auth::user()->school_id)
                ->where('status', 1);
            if ($request->search) {
                $search = $request->search;
                $users = $users->where(function ($query) use ($search) {
                    $query->where('first_name', 'LIKE', "%$search%")
                        ->orWhere('last_name', 'LIKE', "%$search%")
                        ->orWhereRaw("concat(first_name,' ',last_name) LIKE '%" . $search . "%'");
                });
            }
            $users = $users->where(function ($query) use ($roles) {

                foreach ($roles as $role) {

                    // STAFF umbrella
                    if ($role === 'Staff') {
                        $query->orWhereHas('roles', function ($q) {
                            $q->where(function ($r) {
                                $r->where('custom_role', 1);
                            })
                                ->orWhere(function ($r) {
                                    $r->where('custom_role', 0)
                                        ->whereIn('name', ['Driver', 'Helper']);
                                })
                                ->orWhere(function ($r) {
                                    $r->where('name', 'School Admin');
                                });
                        });
                    } else {
                        // Normal roles
                        $query->orWhereHas('roles', function ($q) use ($role) {
                            $q->where('name', $role);
                        });
                    }
                }
            });
            if ($type == 'Over Due Fees') {
                $today = Carbon::now()->format('Y-m-d');
                $users_ids = [];

                $fees = $this->fees->builder()->whereDate('due_date', '<', $today)->get();

                if ($fees->isNotEmpty()) {
                    foreach ($fees as $fee) {
                        $overdueStudents = $this->user->builder()
                            ->role('Student')
                            ->select('id', 'first_name', 'last_name')
                            ->with([
                                'fees_paid' => function ($q) use ($fee) {
                                    $q->where('fees_id', $fee->id);
                                },
                                'student:id,guardian_id,user_id',
                                'student.guardian:id'
                            ])
                            ->whereHas('student.class_section', function ($q) use ($fee) {
                                $q->where('class_id', $fee->class_id);
                            })
                            ->whereDoesntHave('fees_paid', function ($q) use ($fee) {
                                $q->where('fees_id', $fee->id);
                            })
                            ->orWhereHas('fees_paid', function ($q) use ($fee) {
                                $q->where(['fees_id' => $fee->id, 'is_fully_paid' => 0]);
                            })
                            ->get();

                        $users_ids = array_unique(array_merge($overdueStudents->pluck('id')->toArray() ?? [], $overdueStudents->pluck('student.guardian.id')->toArray() ?? []));
                        $users = $this->user->builder()->whereIn("id", $users_ids);

                        if (is_array($roles)) {
                            $users->whereHas('roles', function ($q) use ($roles) {
                                $q->whereIn('name', $roles);
                            });
                        }
                    }
                } else {
                    $users = $this->user->builder()->whereIn("id", $users_ids);
                }
            }
            $users = $users->with('roles')
                ->orderBy('first_name', 'ASC')
                ->paginate(10);

            return ResponseService::successResponse(
                'Users fetched successfully',
                $users
            );
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            return ResponseService::errorResponse();
        }
    }

    public function usersChatHistory(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'role' => 'required|in:Guardian,Staff,Student,Teacher',
        ], [
            'role.required' => 'The role field is mandatory. Please select a role.',
            'role.in' => 'The selected role is invalid. Valid roles are: Guardian, Staff, Student, Teacher.',
        ]);

        if ($validator->fails()) {
            return ResponseService::validationError($validator->errors()->first());
        }

        try {
            $data = [];
            $userId = Auth::id();
            $search = $request->search;

            if (Auth::user()) {

                $data = Chat::where(function ($q) use ($userId) {
                    $q->where('sender_id', $userId)
                        ->orWhere('receiver_id', $userId);
                })
                    ->where(function ($q) use ($request, $userId) {

                        switch ($request->role) {
                            case 'Staff':
                                $roleNames = ['Driver', 'Helper', 'Teacher'];
                                if (Auth::user()->hasRole('Guardian')) {
                                    $roleNames = ['Driver', 'Helper'];
                                }
                                $q->whereHas('receiver', function ($receiverQ) use ($userId, $roleNames) {
                                    $receiverQ->where('id', '!=', $userId)
                                        ->whereHas('roles', function ($roleQ) use ($roleNames) {
                                            $roleQ->where('custom_role', 1)
                                                ->orWhere(function ($q2) use ($roleNames) {
                                                    $q2->where('custom_role', 0)
                                                        ->whereIn('name', $roleNames);
                                                });
                                        });
                                })
                                    ->orWhereHas('sender', function ($senderQ) use ($userId, $roleNames) {
                                        $senderQ->where('id', '!=', $userId)
                                            ->whereHas('roles', function ($roleQ) use ($roleNames) {
                                                $roleQ->where('custom_role', 1)
                                                    ->orWhere(function ($q2) use ($roleNames) {
                                                        $q2->where('custom_role', 0)
                                                            ->whereIn('name', $roleNames);
                                                    });
                                            });
                                    });
                                break;

                            case 'Teacher':
                                $q->whereHas('receiver', function ($receiverQ) use ($userId) {
                                    $receiverQ->where('id', '!=', $userId)
                                        ->whereHas('roles', fn($roleQ) => $roleQ->where('name', 'Teacher'));
                                })
                                    ->orWhereHas('sender', function ($senderQ) use ($userId) {
                                        $senderQ->where('id', '!=', $userId)
                                            ->whereHas('roles', fn($roleQ) => $roleQ->where('name', 'Teacher'));
                                    });
                                break;

                            case 'Student':
                            case 'Guardian':
                                $role = $request->role;
                                $q->whereHas('receiver', function ($receiverQ) use ($role, $userId) {
                                    $receiverQ->where('id', '!=', $userId)
                                        ->role([$role]);
                                })
                                    ->orWhereHas('sender', function ($senderQ) use ($role, $userId) {
                                        $senderQ->where('id', '!=', $userId)
                                            ->role([$role]);
                                    });
                                break;
                        }
                    })
                    ->withCount([
                        'message as unread_count' => function ($q) use ($userId) {
                            $q->where('read_at', null)
                                ->where('sender_id', '!=', $userId);
                        }
                    ])
                    ->when($search, function ($q) use ($search, $userId) {
                        $q->where(function ($query) use ($search, $userId) {
                            // Search in receiver's name
                            $query->whereHas('receiver', function ($receiverQ) use ($search, $userId) {
                                $receiverQ->where('id', '!=', $userId)
                                    ->where(function ($nameQ) use ($search) {
                                        $nameQ->where('first_name', 'LIKE', "%$search%")
                                            ->orWhere('last_name', 'LIKE', "%$search%")
                                            ->orWhereRaw("concat(first_name,' ',last_name) LIKE ?", ["%$search%"]);
                                    });
                            })
                                // Search in sender's name
                                ->orWhereHas('sender', function ($senderQ) use ($search, $userId) {

                                    $senderQ->where('id', '!=', $userId)
                                        ->where(function ($nameQ) use ($search) {
                                            $nameQ->where('first_name', 'LIKE', "%$search%")
                                                ->orWhere('last_name', 'LIKE', "%$search%")
                                                ->orWhereRaw("concat(first_name,' ',last_name) LIKE ?", ["%$search%"]);
                                        });
                                });
                        });
                    })
                    ->paginate(10);
            }
            ResponseService::successResponse("Data Fetched Successfully", $data);
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }


    public function classSectionTeachers(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'class_section_id' => 'required'
        ]);
        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {

            $users = $this->user->builder()->role(['Teacher'])->select('id', 'first_name', 'last_name', 'image')
                ->whereHas('class_teacher', function ($q) use ($request) {
                    $q->where('class_section_id', $request->class_section_id);
                })
                ->orWhereHas('subjectTeachers', function ($q) use ($request) {
                    $q->where('class_section_id', $request->class_section_id);
                })
                // ->with(['class_teacher' => function($q) use($request) {
                //     $q->where('class_section_id', $request->class_section_id);
                // }])
                // ->with(['subjectTeachers' => function($q) use($request) {
                //     $q->where('class_section_id', $request->class_section_id);
                // }])
                ->get();

            ResponseService::successResponse("Data Fetched Successfully", $users);
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }

    public function schoolDetails(Request $request)
    {
        try {
            $gallery_images = [];
            $school_code = $request->header('school-code');

            if ($school_code) {
                $school = School::on('mysql')->where('code', $school_code)->first();

                if ($school) {
                    DB::setDefaultConnection('school');
                    Config::set('database.connections.school.database', $school->database_name);
                    DB::purge('school');
                    DB::connection('school')->reconnect();
                    DB::setDefaultConnection('school');

                    $names = array('school_name', 'school_tagline', 'horizontal_logo');

                    $settings = $this->schoolSettings->getBulkData($names);

                    $gallery = $this->gallery->builder()->with('file')->first();
                    if ($gallery) {
                        $gallery_images = $gallery->file->where('file_name', '!=', 'YouTube Link')->pluck('file_url')->toArray();
                    }

                    $schoolDetails = array(
                        'school_name' => $settings['school_name'],
                        'school_tagline' => $settings['school_tagline'],
                        'school_logo' => $settings['horizontal_logo'],
                        'school_images' => $gallery_images ?? []
                    );
                } else {
                    return response()->json(['message' => 'Invalid school code'], 400);
                }
            } else {
                return response()->json(['message' => 'Unauthenticated'], 400);
            }


            ResponseService::successResponse("Data Fetched Successfully", $schoolDetails);
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            ResponseService::errorResponse();
        }
    }

    public function sendFeeNotification(Request $request)
    {
        try {

            $school_code = $request->header('school-code');

            if ($school_code) {
                $school = School::on('mysql')->where('code', $school_code)->first();

                if ($school) {
                    DB::setDefaultConnection('school');
                    Config::set('database.connections.school.database', $school->database_name);
                    DB::purge('school');
                    DB::connection('school')->reconnect();
                    DB::setDefaultConnection('school');

                    $feesRemainderDuration = $this->schoolSettings->builder()->where('name', 'fees_remainder_duration')->value('data') ?? 2;

                    $feesRemainderDuration = (int) $feesRemainderDuration;

                    if (!$feesRemainderDuration) {
                        return response()->json(['message' => 'Remainder duration not found in settings'], 400);
                    }

                    $classesWithDueDates = $this->fees->builder()->with('installments')->get();

                    $today = Carbon::now();

                    foreach ($classesWithDueDates as $classFee) {

                        $dueDate = Carbon::parse($classFee->due_date);
                        $class_section_id = $this->classSection->builder()->where('class_id', $classFee->class_id)->first();
                        $daysUntilDue = $today->diffInDays($dueDate, false);

                        if ($daysUntilDue <= $feesRemainderDuration && $daysUntilDue >= 0) {

                            $user = $this->student->builder()->whereIn('class_section_id', $class_section_id)->pluck('guardian_id')->toArray();
                            $title = 'Fees Due Reminder';
                            $body = "Pay fees if you didn't paid !!";
                            $type = "fee-reminder";
                            send_notification($user, $title, $body, $type);
                        }
                    }
                } else {
                    return response()->json(['message' => 'Invalid school code'], 400);
                }
            } else {
                return response()->json(['message' => 'Unauthenticated'], 400);
            }


            ResponseService::successResponse("Notification Sent Successfully",);
        } catch (\Throwable $e) {
            ResponseService::logErrorResponse($e);
            return ResponseService::errorResponse();
        }
    }



    public function paymentStatus(Request $request)
    {
        Log::info('Payment Status Callback:', $request->all());
        ResponseService::successResponse("Payment Status Callback.", $request->all());
    }

    public function flutterwaveFeesWebhook(Request $request)
    {
        Log::info('Flutterwave Fees Webhook:', $request->all());
        ResponseService::successResponse("Flutterwave Fees Webhook received.");
    }

    public function flutterwaveSuccessCallback()
    {
        Log::info('Flutterwave Successfully.');
        ResponseService::successResponse("Flutterwave Successfully.");
    }

    public function flutterwaveCancelCallback()
    {
        Log::info('Flutterwave Payment Canceled.');
        ResponseService::successResponse("Flutterwave Payment Canceled.");
    }

    public function getStudentDetails(Request $request)
    {
        try {
            if (!$request->has('student_id') || empty($request->student_id)) {
                return ResponseService::errorResponse("Student ID is required");
            }
            $user = $this->student->builder()
                ->where('user_id', $request->student_id)
                ->with([
                    'user',
                    'class_section.class',
                    'class_section.class.shift',
                    'class_section.section',
                    'class_section.medium'
                ])->first();

            if (!$user) {
                return ResponseService::errorResponse("Student not found");
            }

            // Add semester subjects as a property
            $user->subjects = $user->currentSemesterSubjects();

            ResponseService::successResponse("Student Class Details Fetched Successfully", $user);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function pickupPoints(Request $request)
    {
        try {
            $school_code = $request->header('school-code');

            if (!$school_code) {
                return response()->json(['message' => 'Unauthenticated'], 400);
            }

            $school = School::on('mysql')->where('code', $school_code)->first();

            if (!$school) {
                return response()->json(['message' => 'Invalid school code'], 400);
            }

            // Switch DB
            DB::setDefaultConnection('school');
            Config::set('database.connections.school.database', $school->database_name);
            DB::purge('school');
            DB::connection('school')->reconnect();
            DB::setDefaultConnection('school');

            $pickupPoints = $this->pickupPoint->builder()->select('id', 'name', 'latitude', 'longitude')->where('status', 1)->get();

            return ResponseService::successResponse("Pickup points fetched successfully", $pickupPoints);
        } catch (\Throwable $th) {
            ResponseService::logErrorResponse($th);
            return ResponseService::errorResponse();
        }
    }

    public function getStudentDiaries(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'student_id' => 'required',
        ]);

        if ($validator->fails()) {
            return ResponseService::validationError($validator->errors()->first());
        }

        try {
            /* -------------------------------------------------
             | 1. Fetch student → class_section → class_id
             -------------------------------------------------*/
            $student = $this->user->builder()
                ->where('id', $request->student_id)
                ->with('student.class_section') // student → class_section
                ->first();

            if (
                !$student ||
                !$student->student ||
                !$student->student->class_section ||
                !$student->student->class_section->class_id
            ) {
                return ResponseService::errorResponse('Invalid student or class not found');
            }

            $classId = $student->student->class_section->class_id;
            $sortType = strtolower($request->get('sort', 'new'));

            /* -------------------------------------------------
             | 2. Resolve allowed class_subject_ids
             -------------------------------------------------*/
            $allowedClassSubjectIds = DB::table('class_subjects')
                ->where('class_id', $classId)
                ->where(function ($q) use ($request) {

                    // Compulsory subjects → always allowed
                    $q->where('type', 'Compulsory')

                        // Elective subjects → must be assigned
                        ->orWhere(function ($q) use ($request) {
                            $q->where('type', 'Elective')
                                ->whereExists(function ($sub) use ($request) {
                                    $sub->select(DB::raw(1))
                                        ->from('student_subjects')
                                        ->whereColumn(
                                            'student_subjects.class_subject_id',
                                            'class_subjects.id'
                                        )
                                        ->where('student_subjects.student_id', $request->student_id);
                                });
                        });
                })
                ->pluck('id');

            /* -------------------------------------------------
             | 3. Base student query
             -------------------------------------------------*/
            $users = $this->user->builder()
                ->select('id', 'first_name', 'last_name', 'mobile', 'email', 'image', 'dob')
                ->where('id', $request->student_id)
                ->whereHas('roles', function ($q) {
                    $q->where('name', 'Student');
                });

            // Search
            if ($request->search) {
                $users->where(function ($q) use ($request) {
                    $q->where('first_name', 'like', "%{$request->search}%")
                        ->orWhere('last_name', 'like', "%{$request->search}%")
                        ->orWhere('mobile', 'like', "%{$request->search}%")
                        ->orWhere('email', 'like', "%{$request->search}%");
                });
            }

            /* -------------------------------------------------
             | 4. Diary student filter
             -------------------------------------------------*/
            $diaryStudentFilter = function ($q) use ($request, $sortType, $allowedClassSubjectIds) {

                // Diary category
                if ($request->diary_category_id) {
                    $q->whereHas('diary', function ($dq) use ($request) {
                        $dq->where('diary_category_id', $request->diary_category_id);
                    });
                }

                // Positive / Negative
                if ($sortType === 'positive') {
                    $q->whereHas('diary.diary_category', function ($dq) {
                        $dq->where('type', 'positive');
                    });
                }

                if ($sortType === 'negative') {
                    $q->whereHas('diary.diary_category', function ($dq) {
                        $dq->where('type', 'negative');
                    });
                }

                // SUBJECT FILTER (class_subject based)
                $q->where(function ($subjectFilter) use ($allowedClassSubjectIds) {

                    // Case 1: Diary has NO subject (subject_id IS NULL)
                    $subjectFilter->whereDoesntHave('diary.subject')

                        // Case 2: Diary has subject AND it is allowed
                        ->orWhereHas('diary.subject.class_subjects', function ($cs) use ($allowedClassSubjectIds) {
                            $cs->whereIn('id', $allowedClassSubjectIds);
                        });
                });

                // Optional subject filter
                if ($request->subject_id) {
                    $q->whereHas('diary.subject', function ($sq) use ($request) {
                        $sq->where('subjects.id', $request->subject_id);
                    });
                }

                // Sorting
                if ($sortType === 'new') {
                    $q->orderBy('created_at', 'DESC');
                }

                if ($sortType === 'old') {
                    $q->orderBy('created_at', 'ASC');
                }
            };

            /* -------------------------------------------------
             | 5. Apply filters + eager loading
             -------------------------------------------------*/
            $users->whereHas('diary_student', $diaryStudentFilter);

            $users->with([
                'diary_student' => $diaryStudentFilter,
                'diary_student.diary.subject',
                'diary_student.diary.diary_category' => function ($q) {
                    $q->withTrashed();
                }
            ]);

            /* -------------------------------------------------
             | 6. Response
             -------------------------------------------------*/
            $result = $users
                ->orderBy('id', 'DESC')
                ->paginate(10);

            ResponseService::successResponse(
                'Student Diaries Fetched Successfully',
                $result
            );
        } catch (\Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getTeachers(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'student_id' => 'required|numeric',
        ]);

        if ($validator->fails()) {
            ResponseService::validationError($validator->errors()->first());
        }
        try {
            // Get student by user_id (student_id is the user_id)
            $student = $this->student->builder()
                ->where('user_id', $request->student_id)
                ->whereHas('user', function ($q) {
                    $q->whereNull('deleted_at');
                })
                ->first();

            if (empty($student)) {
                ResponseService::errorResponse("Student Account is not Active. Contact School Support", NULL, config('constants.RESPONSE_CODE.INACTIVE_CHILD'));
            }

            $defaultSessionYear = $this->cache->getDefaultSessionYear();

            // Get class subject IDs for the student
            $class_subject_id = $student->selectedStudentSubjects()->pluck('class_subject_id');

            // Get subject teachers for the student's class section and subjects
            $subjectTeachers = SubjectTeacher::select('id', 'subject_id', 'teacher_id', 'school_id')
                ->whereIn('class_subject_id', $class_subject_id)
                ->where('class_section_id', $student->class_section_id)
                ->whereHas('teacher', fn($q) => $q->where('status', 1))
                ->where('session_year_id', $defaultSessionYear->id)
                ->with([
                    'subject:id,name,type',
                    'teacher:id,first_name,last_name,image,mobile'
                ])
                ->get();

            ResponseService::successResponse("Teacher Details Fetched Successfully", $subjectTeachers);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            ResponseService::errorResponse();
        }
    }

    public function getNotifications(Request $request)
    {
        try {

            $offset = $request->offset ?? 0;
            $limit = $request->limit ?? 10;
            $user = Auth::user();
            if (!$user) {
                return ResponseService::errorResponse("User not authenticated");
            }

            // Only notifications belonging to this user (directly attached)
            $user_notifications = UserNotification::where('user_id', $user->id)->pluck('notification_id')->toArray();
            $notifications = Notification::whereIn('id', $user_notifications)->orderBy('id', 'DESC')->offset($offset)->limit($limit)->get();

            return ResponseService::successResponse("Notifications Fetched Successfully", $notifications);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            return ResponseService::errorResponse();
        }
    }

    public function getFirebaseConfig()
    {
        try {
            // Fetch Firebase configuration fields individually and decode HTML entities
            $firebaseConfig = [
                'firebase_api_key' => htmlspecialchars_decode($this->cache->getSystemSettings('firebase_api_key') ?? ''),
                'firebase_auth_domain' => htmlspecialchars_decode($this->cache->getSystemSettings('firebase_auth_domain') ?? ''),
                'firebase_storage_bucket' => htmlspecialchars_decode($this->cache->getSystemSettings('firebase_storage_bucket') ?? ''),
                'firebase_messaging_sender_id' => htmlspecialchars_decode($this->cache->getSystemSettings('firebase_messaging_sender_id') ?? ''),
                'firebase_app_id' => htmlspecialchars_decode($this->cache->getSystemSettings('firebase_app_id') ?? ''),
                'firebase_measurement_id' => htmlspecialchars_decode($this->cache->getSystemSettings('firebase_measurement_id') ?? ''),
                'firebase_service_file' => htmlspecialchars_decode($this->cache->getSystemSettings('firebase_service_file') ?? ''),
                'firebase_project_id' => htmlspecialchars_decode($this->cache->getSystemSettings('firebase_project_id') ?? '')
            ];

            return ResponseService::successResponse("Data Fetched Successfully", $firebaseConfig);
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            return ResponseService::errorResponse();
        }
    }

    public function getSystemSettings(Request $request)
    {
        try {
            $school_code = $request->school_code;

            if ($school_code) {
                $school = School::on('mysql')->where('code', $school_code)->first();
                if ($school) {

                    //   // Switch DB
                    DB::setDefaultConnection('school');
                    Config::set('database.connections.school.database', $school->database_name);
                    DB::purge('school');
                    DB::connection('school')->reconnect();
                    DB::setDefaultConnection('school');

                    $data = [
                        'vertical_logo' => $this->cache->getSchoolSettings('vertical_logo', $school->id) ?? '',
                        'horizontal_logo' => $this->cache->getSchoolSettings('horizontal_logo', $school->id) ?? '',
                        'favicon' => $this->cache->getSchoolSettings('favicon', $school->id) ?? '',
                        'student_web_background_image' => $this->cache->getSystemSettings('student_web_background_image') ?? '',
                        'system_name' => $this->cache->getSchoolSettings('system_name', $school->id) ?? '',
                        'address' => $this->cache->getSystemSettings('address', $school->id) ?? '',
                        'tag_line' => $this->cache->getSchoolSettings('tag_line', $school->id) ?? '',
                        'theme_color' => $this->cache->getSchoolSettings('theme_color', $school->id) ?? '',
                        'mobile' => $this->cache->getSchoolSettings('mobile', $school->id) ?? '',
                        'student_parent_privacy_policy' => url('page/student-parent-privacy-policy') ?? '',
                        'student_terms_condition' => url('page/student-terms-conditions') ?? '',
                        'student_web_maintenance' => $this->cache->getSystemSettings('student_web_maintenance') ?? '0',
                    ];

                    return ResponseService::successResponse("System Settings Fetched Successfully", $data);
                } else {
                    return ResponseService::errorResponse("School not found");
                }
            } else {
                $data = [
                    'vertical_logo' => $this->cache->getSystemSettings('vertical_logo') ?? '',
                    'horizontal_logo' => $this->cache->getSystemSettings('horizontal_logo') ?? '',
                    'favicon' => $this->cache->getSystemSettings('favicon') ?? '',
                    'student_web_background_image' => $this->cache->getSystemSettings('student_web_background_image') ?? '',
                    'system_name' => $this->cache->getSystemSettings('system_name') ?? '',
                    'address' => $this->cache->getSystemSettings('address') ?? '',
                    'tag_line' => $this->cache->getSystemSettings('tag_line') ?? '',
                    'theme_color' => $this->cache->getSystemSettings('theme_color') ?? '',
                    'mobile' => $this->cache->getSystemSettings('mobile') ?? '',
                    'student_parent_privacy_policy' => url('page/student-parent-privacy-policy') ?? '',
                    'student_terms_condition' => url('page/student-terms-conditions') ?? '',
                    'student_web_maintenance' => $this->cache->getSystemSettings('student_web_maintenance') ?? '1',
                ];
                return ResponseService::successResponse("System Settings Fetched Successfully", $data);
            }
        } catch (Throwable $e) {
            ResponseService::logErrorResponse($e);
            return ResponseService::errorResponse();
        }
    }
}