<?php

namespace App\Controllers\View;

use App\Controllers\BaseController;
use App\Models\UserModel;
use App\Models\StudentModel;
use App\Models\ClassSectionModel;
use App\Models\StudentClassModel;
use App\Models\StudentSectionDistributionDraftModel;
use App\Models\AuthorizedUserModel;
use App\Models\EmergencyContactModel;
use App\Models\ConfigurationModel;
use App\Models\StudentMedicalConditionModel;
use App\Models\StudentAllergyModel;
use \App\Models\EnrollmentModel;
use \App\Models\EventChargesModel;
use \App\Models\EventModel;
use CodeIgniter\Events\Events;
use App\Services\SchoolIdService;
use App\Services\FeeCalculationService;
use App\Services\PhoneFormatterService;
use InvalidArgumentException;
use DateTimeImmutable;
use DateTimeZone;
use Throwable;


use DateTime;
use Exception;
use CodeIgniter\Database\Exceptions\DatabaseException;


class ParentController extends BaseController
{
    protected $userModel;
    protected $studentModel;
    protected $db;
    protected $classSectionModel;
    protected $studentClassModel;
    protected $configModel;
    protected $lastDayOfRegistration;
    protected $withdrawalDeadline;
    protected $schoolYear;
    protected $semester;
    protected $dateAgeReference;
    protected $enrollmentModel;
    protected $maxChilds;
    protected $maxEmergency;
    protected $chargesModel;
    protected $eventModel;
    protected $eventController;
    protected $emergencyContactModel;
    protected $medicalConditionModel;
    protected $allergyModel;
    protected $authorizedUsersModel;
    protected $schoolStartDate;
    protected $ageDateRefernce;



    public function __construct()
    {
        $this->db = \Config\Database::connect();
        $this->userModel = new UserModel();
        $this->studentModel = new StudentModel();
        $this->classSectionModel = new ClassSectionModel();
        $this->studentClassModel = new StudentClassModel();
        $this->configModel = new ConfigurationModel();
        $this->enrollmentModel = new EnrollmentModel();
        $this->chargesModel = new EventChargesModel();
        $this->eventModel = new EventModel();
        $this->eventController = new \App\Controllers\View\InvoiceController();
        $this->emergencyContactModel =  new EmergencyContactModel();
        $this->medicalConditionModel = new StudentMedicalConditionModel();
        $this->allergyModel = new StudentAllergyModel();
        $this->authorizedUsersModel = new AuthorizedUserModel();

        $this->ageDateRefernce = $this->configModel->getConfig('date_age_reference');
        $this->lastDayOfRegistration = $this->configModel->getConfig('enrollment_deadline');
        $this->schoolStartDate = $this->configModel->getConfig('fall_semester_start');
        $this->withdrawalDeadline = $this->configModel->getConfig('refund_deadline');
        $this->schoolYear = $this->configModel->getConfig('school_year');
        $this->semester = $this->configModel->getConfig('semester');
        $this->dateAgeReference     = $this->configModel->getConfig('date_age_reference');
        $this->maxChilds       = (int) $this->configModel->getConfig('max_kids') ?? 0;
        $this->maxEmergency    = (int) $this->configModel->getConfig('max_emergency') ?? 0;

        helper(['url', 'form']);

        if (!session()->get('is_logged_in')) {
            return redirect()->to('/login');
        }

        // Add more role-specific checks if needed
        if (session()->get('role') !== 'parent') {
            return redirect()->to('/access_denied');
        }
    }

    public function index()
    {
        // Retrieve all parents from the database where role is parent
        $parents = $this->userModel->where('role', 'parent')->findAll();
        // Pass the parents to the view
        return view('administrator/parent', ['parents' => $parents]);
    }

    public function create()
    {
        // Show the form to create a new parent
        return view('administrator/create_parent');
    }

    public function store()
    {
        // Handle the form submission to create a new parent
        $data = [
            'firstname' => $this->request->getPost('firstname'),
            'lastname' => $this->request->getPost('lastname'),
            'email' => strtolower($this->request->getPost('email')),
            'password' => password_hash($this->request->getPost('password'), PASSWORD_DEFAULT),
            'role' => 'parent',
        ];
        $this->userModel->insert($data);
        return redirect()->to('/administrator/parent');
    }

    public function edit($id)
    {
        // Retrieve the parent details to edit
        $parent = $this->userModel->find($id);
        return view('administrator/edit_parent', ['parent' => $parent]);
    }

    public function update($id)
    {
        // Handle the form submission to update an existing parent
        $data = [
            'firstname' => $this->request->getPost('firstname'),
            'lastname' => $this->request->getPost('lastname'),
            'email' => strtolower($this->request->getPost('email')),
        ];
        if ($this->request->getPost('password')) {
            $data['password'] = password_hash($this->request->getPost('password'), PASSWORD_DEFAULT);
        }
        $this->userModel->update($id, $data);
        return redirect()->to('/administrator/parent');
    }

    public function destroy($id)
    {
        // Delete the parent
        $this->userModel->delete($id);
        return redirect()->to('/administrator/parent');
    }

    public function attendance()
    {
        try {
            // Get parent ID from session
            $parentId = session()->get('user_id');

            if (!$parentId) {
                return redirect()->back()->with('error', 'Parent session not found.');
            }

            $selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));

            // Build query to retrieve attendance
            $builder = $this->db->table('attendance_data');
            $builder->select('students.firstname, students.lastname, attendance_data.date, attendance_data.status, attendance_data.reason');
            $builder->join('students', 'students.id = attendance_data.student_id');
            $builder->where('attendance_data.school_year', $selectedYear);
            // No semester filter so both semesters show for the selected year
            $builder->where('students.parent_id', $parentId);

            $query = $builder->get();
            $attendanceResults = $query->getResultArray();

            // If no records found, set a flag to show message in the view
            if (empty($attendanceResults)) {
                return view('/parent/attendance', [
                    'attendance' => null,
                    'selectedYear' => $selectedYear,
                    'selectedSemester' => null,
                    //'error' => 'No attendance records found for the selected school year and semester.'
                ]);
            }

            // Return view with attendance results
            return view('/parent/attendance', [
                'attendance' => $attendanceResults,
                'selectedYear' => $selectedYear,
                'selectedSemester' => null,
                'error' => null
            ]);
        } catch (Exception $e) {
            log_message('error', 'Failed to retrieve attendance data: ' . $e->getMessage());

            return view('/parent/attendance', [
                'attendance' => null,
                'selectedYear' => $selectedYear ?? null,
                'selectedSemester' => null,
                'error' => 'Failed to retrieve attendance data. Please try again later.'
            ]);
        }
    }
    public function viewPayments()
    {
        $parentId = session()->get('user_id');

        // Log the user ID for debugging
        log_message('info', 'Logged in user ID: ' . $parentId);

        if (!$parentId) {
            log_message('error', 'No user ID found in session.');
            return view('/parent/payment', ['invoices' => [], 'error' => 'No user ID found in session.']);
        }

        // Fetch invoices for the logged-in user based on parent IDs
        $builder = $this->db->table('invoices');
        $builder->select('*');
        $builder->groupStart()
            ->where('parent_id', $parentId)
            //->orWhere('secondparent_user_id', $userId)
            ->groupEnd();
        $query = $builder->get();
        $invoices = $query->getResultArray();

        // Log the query and results for debugging
        log_message('info', 'Query executed: ' . $builder->getCompiledSelect());
        log_message('info', 'invoices: ' . print_r($invoices, true));

        if (empty($invoices)) {
            log_message('info', 'No invoices found for user ID: ' . $parentId);
        } else {
            log_message('info', count($invoices) . ' payment(s) found for user ID: ' . $parentId);
        }

        // Pass the data to the view
        return view('/parent/payment', [
            'invoices' => $invoices
        ]);
    }

    public function enrollClasses()
    {
        try {
            // Log session data for debugging
            log_message('info', 'Session Data: ' . print_r(session()->get(), true));

            // Get deadlines and school year from config
            if (!$this->schoolYear) {
                log_message('error', 'Current school year not found in configuration.');
                return redirect()->back()->with('error', 'Configuration error: School year missing.');
            }

            $context = $this->resolveSchoolYearContext();
            $selectedYear = $context->yearName();
            $isEditable = ! $context->isReadonly();

            // Get parent ID from session
            $parentId = session()->get('user_id');

            if (!$parentId) {
                log_message('error', 'User ID not found in session.');
                return redirect()->back()->with('error', 'User session error. Please log in again.');
            }

            // Verify user type is "parent" from the `users` table
            $userData = $this->db->table('users')
                ->select('user_type')
                ->where('id', $parentId)
                ->get()
                ->getRowArray();

            if (!$userData || $userData['user_type'] !== 'primary') {
                return redirect()->back()->with('error', 'Invalid user type. Only primary parents can enroll.');
            }

            // Fetch students with parent_id = current logged in user
            $students = $this->db->table('students')
                ->where('parent_id', $parentId)
                ->get()
                ->getResultArray();

            if (empty($students)) {
                log_message('error', 'No students found for Parent ID: ' . $parentId);
                return redirect()->to('/no-kids')->with('error', 'No students found. Please register your child first.');
            }

            // Map enrollment statuses
            $statusMap = [
                'admission under review' => 'admission under review',
                'payment pending' => 'payment pending',
                'enrolled' => 'enrolled',
                'withdraw under review' => 'withdraw under review',
                'refund pending' => 'refund pending',
                'withdrawn' => 'withdrawn',
                'denied' => 'denied',
                'waitlist' => 'waitlist' // ✅ Add this line
            ];


            // Attach enrollment and class section data to each student
            foreach ($students as &$student) {
                $studentId = $student['id'];
                $student['age'] = $this->calculateAgeAsOfSchoolYearStartYear($student['dob'] ?? null, $selectedYear);

                // Get class section info (can be multiple sections like Grade + Arabic)
                $classSections = $this->studentClassModel->getClassSectionsByStudentId($studentId, $selectedYear, true);
                $student['class_section'] = !empty($classSections)
                    ? implode(', ', $classSections)
                    : 'Class not Assigned';
                $isArabicClass = !empty($classSections) && array_reduce(
                    $classSections,
                    static fn($carry, $name) => $carry || (is_string($name) && stripos($name, 'arabic') === 0),
                    false
                );

                // ✅ Get enrollment status AND admission status
                $enrollment = $this->db->table('enrollments')
                    ->select('enrollment_status, admission_status')
                    ->where('student_id', $studentId)
                    ->where('school_year', $selectedYear)
                    ->get()
                    ->getRowArray();

                // ✅ Apply admission status override logic
                if ($enrollment && isset($enrollment['admission_status'])) {
                    $student['admission_status'] = $enrollment['admission_status'];

                    if ($enrollment['admission_status'] === 'denied') {
                        // Force enrollment_status to denied if admission is denied
                        $student['enrollment_status'] = 'denied';
                    } else {
                        // Use enrollment_status from DB if admission is accepted/pending
                        $student['enrollment_status'] = $enrollment && isset($statusMap[$enrollment['enrollment_status']])
                            ? $statusMap[$enrollment['enrollment_status']]
                            : 'not enrolled';
                    }
                } else {
                    // No enrollment record found
                    $student['admission_status'] = null;
                    $student['enrollment_status'] = 'not enrolled';
                }

                // If assigned to Arabic class without an enrollment record, display as enrolled.
                if ($student['enrollment_status'] === 'not enrolled' && $isArabicClass) {
                    $student['enrollment_status'] = 'enrolled';
                }

                // ✅ Updated disable logic to include denied status
                $student['disable_enroll'] = in_array(
                    $student['enrollment_status'],
                    ['admission under review', 'payment pending', 'enrolled', 'withdraw under review', 'denied']
                );
            }

            // Render view
            return view('/parent/enroll_classes', [
                'students' => $students,
                'selectedYear' => $selectedYear,
                'isEditable' => $isEditable,
                'withdrawalDeadline' => $this->withdrawalDeadline,
                'lastDayOfRegistration' => $this->lastDayOfRegistration,
                'schoolStartDate' => $this->schoolStartDate
            ]);
        } catch (Exception $e) {
            log_message('error', 'An error occurred in enrollClasses: ' . $e->getMessage());
            return view('errors/html/error_500');
        }
    }

    public function noKids()
    {
        return view('/parent/no_kids_registred');
    }

    public function enrollClassesHandler()
    {
        // Call enrollClasses() function at the start of this method
        $this->enrollClasses();
        $refundService = new FeeCalculationService();

        // Retrieve enrollment and withdrawal data from the POST request
        $enroll = $this->request->getPost('enroll'); // Selected students for enrollment
        $withdraw = $this->request->getPost('withdraw'); // Selected students for withdrawal
        $parentId = session()->get('user_id'); // Parent ID from session

        if ($this->lastDayOfRegistration && local_date(utc_now(), 'Y-m-d') > date('Y-m-d', strtotime($this->lastDayOfRegistration))) {
            if (!empty($enroll)) {
                return redirect()->back()->with('error', 'Enrollment deadline has passed. New enrollments are not allowed.');
            }
        }

        // Ensure there are students selected for either enrollment or withdrawal
        if (empty($enroll) && empty($withdraw)) {
            return redirect()->back()->with('error', 'No students selected for enrollment or withdrawal.');
        }

        // Handle enrollments
        $studentData = [];

        if (!empty($enroll)) {
            foreach ($enroll as $studentId) {
                // Get student full name (supports both string return or array with firstname/lastname)
                $studentInfo = $this->studentModel->getFullNameById($studentId);

                if (empty($studentName)) {
                    $studentName = "Student ID $studentId";
                    log_message('warning', "Name for student ID $studentId not found in students table.");
                }

                // Save student info into $studentData
                $studentData[$studentId] =  $studentInfo;   // raw return from getFullName()

                // Check if the student already has an enrollment record for the current school year and semester
                $existingEnrollment = $this->enrollmentModel
                    ->where('student_id', $studentId)
                    ->where('school_year', $this->schoolYear)
                    ->where('semester', $this->semester)
                    ->get()
                    ->getRowArray();

                $studentSchoolId = $this->studentModel->getStudentSchoolIdByStudentId($studentId);
                if (!$studentSchoolId) {
                    return redirect()->back()->with('error', "{$studentData[$studentId]['firstname']} {$studentData[$studentId]['lastname']}: Student school ID not found.");
                }
                if ($existingEnrollment) {
                    if ($existingEnrollment['is_withdrawn'] == 1) {
                        $passedPreviousYear = $this->studentPassedPreviousYear((int)$studentId, (string)$this->schoolYear);

                        // Reactivate the enrollment if the student was previously withdrawn
                        $this->enrollmentModel->where('id', $existingEnrollment['id'])->update([
                            'is_withdrawn' => 0,
                            'withdrawal_date' => null,
                            'enrollment_status' => $passedPreviousYear ? 'payment pending' : 'admission under review',
                            'admission_status' => $passedPreviousYear ? 'accepted' : 'pending',
                            'updated_at' => utc_now()
                        ]);
                        log_message('info', "{$studentData[$studentId]['firstname']} {$studentData[$studentId]['lastname']} (ID $studentId) has been re-enrolled in enrollment ID {$existingEnrollment['id']}.");
                        // Apply promotion-based class placement for the upcoming year
                        $this->applyPromotionAssignment((int)$studentId, (string)$this->schoolYear);
                    } else {
                        log_message('info', "{$studentData[$studentId]['firstname']} {$studentData[$studentId]['lastname']} (ID $studentId) is already actively enrolled.");
                        $this->applyPromotionAssignment((int)$studentId, (string)$this->schoolYear);
                    }
                } else {
                    $passedPreviousYear = $this->studentPassedPreviousYear((int)$studentId, (string)$this->schoolYear);

                    // If no enrollment record exists, insert a new enrollment record
                    $result = $this->enrollmentModel->insert([
                        'student_id'        => $studentId,
                        'parent_id'         => $parentId,
                        'school_year'       => $this->schoolYear,
                        'semester'          => $this->semester,
                        'enrollment_date'   => local_date(utc_now(), 'Y-m-d'),
                        'is_withdrawn'      => 0,
                        'enrollment_status' => $passedPreviousYear ? 'payment pending' : 'admission under review',
                        'admission_status'  => $passedPreviousYear ? 'accepted' : 'pending',
                        'created_at'        => utc_now()
                    ]);

                    if (!$result) {
                        dd($this->enrollmentModel->errors());
                    } else {
                        log_message('info', "{$studentData[$studentId]['firstname']} {$studentData[$studentId]['lastname']} (ID $studentId) has been newly enrolled.");
                        // Apply promotion-based class placement for the upcoming year
                        $this->applyPromotionAssignment((int)$studentId, (string)$this->schoolYear);
                    }
                }
            }
        }

        // $studentData now holds info for all students processed

        // Handle withdrawals
        if (!empty($withdraw)) {
            foreach ($withdraw as $studentId) {
                $enrollment = $this->enrollmentModel
                    ->where('student_id', $studentId)
                    ->where('school_year', $this->schoolYear)
                    ->where('semester', $this->semester)
                    ->where('is_withdrawn', 0) // Only withdraw active enrollments
                    ->get()
                    ->getRowArray();

                if ($enrollment !== null) {
                    // Update enrollment as withdrawn
                    $this->enrollmentModel->where('id', $enrollment['id'])->update([
                        'withdrawal_date' => local_date(utc_now(), 'Y-m-d'),
                        'enrollment_status' => 'withdraw under review', // Withdrawal needs review
                        'updated_at' => utc_now()
                    ]);
                    log_message('info', "Student ID $studentId has been withdrawn from enrollment ID {$enrollment['id']}.");

                    // === Trigger refund process ===
                    // Find the related invoice (you may need to adjust this based on your DB structure)
                    $invoice = $this->db->table('invoices')
                        ->where('parent_id', $parentId)
                        ->where('school_year', $this->schoolYear)
                        ->orderBy('created_at', 'DESC')
                        ->get()
                        ->getRowArray();

                    if ($invoice !== null) {
                        $invoiceId = $invoice['id'];

                        $refundTable = $this->db->table('refunds');

                        $existingRefund = $refundTable
                            ->where('parent_id', $parentId)
                            ->where('invoice_id', $invoiceId)
                            ->where('school_year', $this->schoolYear)
                            ->get()
                            ->getRow();

                        if ($existingRefund) {
                            // Only update the fields that should change
                            $updateData = [
                                'reason'    => 'Withdrawal under review for student ID ' . $studentId,
                                'note'      => null,
                                'updated_by' => session()->get('user_id'), // optionally track updates
                                // Add other fields if *and only if* they must be changed
                            ];

                            $refundTable
                                ->where('id', $existingRefund->id)
                                ->update($updateData);

                            log_message('info', "Refund record updated for invoice ID {$invoiceId}, student ID {$studentId}.");
                        } else {
                            // Only set these once, for new entries
                            $insertData = [
                                'parent_id'     => $parentId,
                                'invoice_id'    => $invoiceId,
                                'requested_at'  => utc_now(),
                                'school_year'   => $this->schoolYear,
                                'status'        => 'Pending review',
                                'reason'        => 'Withdrawal under review for student ID ' . $studentId,
                                'request'       => 'new',
                                'semester'      => $this->semester,
                                'refund_paid_amount'   => 0.0,
                            ];

                            $refundTable->insert($insertData);

                            log_message('info', "Refund record created for invoice ID {$invoiceId}, student ID {$studentId}.");
                        }
                    } else {
                        log_message('error', "No invoice found for parent ID {$parentId}, student ID {$studentId}.");
                    }
                } else {
                    log_message('error', "No active enrollment found for student ID $studentId.");
                }
            }
        }

        $parentData = $this->userModel->getUserInfoById($parentId);
        $parentData['user_id'] = $parentId;
        // Redirect to the success page after processing enrollment and withdrawal
        if (!empty($withdraw)) {
            // Redirect to withdrawal success page if there are withdrawals //parent/enroll_classes
            return redirect()->to('/parent/enroll_classes');
        } else {
            // Redirect to enrollment success page if there are enrollments
            Events::trigger('admissionUnderReview', $parentData, $studentData);  //send notification for enrolled student
            return redirect()->to('/parent/enroll_classes');
        }
    }

    /**
     * If a promotion_queue record exists for this student and the given school year,
     * create/update student_class row accordingly (base section until distribution),
     * and mark the queue row as applied.
     */
    private function applyPromotionAssignment(int $studentId, string $year): void
    {
        try {
            $promo = new \App\Models\PromotionQueueModel();
            $classSectionModel = new \App\Models\ClassSectionModel();
            $draftModel = new StudentSectionDistributionDraftModel();
            $studentClass = new \App\Models\StudentClassModel();

            $draft = $draftModel->where('student_id', $studentId)
                ->where('school_year', $year)
                ->where('status', 'pending')
                ->first();

            $row = $promo->where('student_id', $studentId)
                ->where('school_year_to', $year)
                ->first();

            if (!$row && !$draft) return; // nothing to do

            $targetSectionId = (int)($draft['class_section_id'] ?? 0);
            if ($targetSectionId <= 0) {
                $targetSectionId = (int)($row['to_class_section_id'] ?? 0);
            }
            if ($targetSectionId <= 0) {
                // Resolve base section for target class (e.g., '3')
                $base = $classSectionModel->getBaseSectionByClassId((int)($row['to_class_id'] ?? 0));
                if (!$base) {
                    log_message('warning', 'applyPromotionAssignment: no draft or base section found for student_id=' . $studentId . ', year=' . $year);
                    return;
                }
                $targetSectionId = (int)($base['class_section_id'] ?? 0);
            }

            if ($targetSectionId <= 0) return;

            // Upsert into student_class for the target school year
            $exists = $studentClass->where('student_id', $studentId)
                ->where('school_year', $year)
                ->first();

            $payload = [
                'student_id'       => $studentId,
                'class_section_id' => $targetSectionId,
                'school_year'      => $year,
                'updated_by'       => (int)(session()->get('user_id') ?? 0) ?: null,
                'updated_at'       => utc_now(),
            ];

            if ($exists) {
                $studentClass->update((int)$exists['id'], $payload);
            } else {
                $payload['created_at'] = utc_now();
                $studentClass->insert($payload);
            }

            $this->db->table('enrollments')
                ->where('student_id', $studentId)
                ->where('school_year', $year)
                ->whereIn('enrollment_status', ['payment pending', 'enrolled'])
                ->update([
                    'class_section_id' => $targetSectionId,
                    'updated_at'       => utc_now(),
                ]);

            // Mark promotion as applied when promotion_queue is the source.
            if ($row) {
                $promo->update((int)$row['id'], [
                    'status'     => 'applied',
                    'updated_at' => utc_now(),
                    'updated_by' => (int)(session()->get('user_id') ?? 0) ?: null,
                ]);
            }

            if ($draft) {
                $draftModel->update((int)$draft['id'], [
                    'status'     => 'applied',
                    'applied_at' => utc_now(),
                    'updated_at' => utc_now(),
                ]);
            }
        } catch (\Throwable $e) {
            log_message('error', 'applyPromotionAssignment failed: ' . $e->getMessage());
        }
    }

    private function studentPassedPreviousYear(int $studentId, string $targetSchoolYear): bool
    {
        $previousSchoolYear = $this->previousSchoolYearName($targetSchoolYear);

        if ($studentId <= 0 || $previousSchoolYear === null) {
            return false;
        }

        try {
            if ($this->db->tableExists('promotion_queue')) {
                $queuedPromotion = $this->db->table('promotion_queue')
                    ->select('id')
                    ->where('student_id', $studentId)
                    ->where('school_year_from', $previousSchoolYear)
                    ->where('school_year_to', $targetSchoolYear)
                    ->whereIn('status', ['queued', 'assigned', 'applied'])
                    ->limit(1)
                    ->get()
                    ->getRowArray();

                if ($queuedPromotion !== null) {
                    return true;
                }
            }

            if ($this->db->tableExists('student_decisions')) {
                $passedDecision = $this->db->table('student_decisions')
                    ->select('id')
                    ->where('student_id', $studentId)
                    ->where('school_year', $previousSchoolYear)
                    ->where('LOWER(TRIM(decision)) = ' . $this->db->escape('pass'), null, false)
                    ->limit(1)
                    ->get()
                    ->getRowArray();

                return $passedDecision !== null;
            }
        } catch (\Throwable $e) {
            log_message('error', 'studentPassedPreviousYear failed: ' . $e->getMessage());
        }

        return false;
    }

    private function previousSchoolYearName(string $schoolYear): ?string
    {
        $schoolYear = trim($schoolYear);

        if (! preg_match('/^(\d{4})-(\d{4})$/', $schoolYear, $matches)) {
            return null;
        }

        return ((int) $matches[1] - 1) . '-' . ((int) $matches[2] - 1);
    }

    public function enrollFailure()
    {
        echo view('/parent/enroll_failure');
    }

    public function payment()
    {
        // Get the logged-in user's ID
        $userId = session()->get('user_id');

        // Fetch invoices records where the user is either the first or second parent
        $invoices = $this->db->table('invoices')
            ->select('invoices.*, registeredKids') // Assuming 'registeredKids' is a field
            ->where('parent_id', $userId)
            //->orWhere('secondparent_user_id', $userId)
            ->get()
            ->getResultArray();

        // Pass the invoices data to the view
        return view('/parent/payment', [
            'invoices' => $invoices
        ]);
    }

    public function paymentHandler()
    {
        $parentId = session()->get('user_id');

        // Update all students for this parent to mark tuition as paid
        $this->db->table('students')
            ->where('parent_id', $parentId)
            ->update(['tuition_paid' => 1]);

        // Clear the total tuition fee from the session
        session()->remove('total_tuition_fee');

        // Set success message in session
        session()->setFlashdata('success', 'Tuition fees paid successfully');

        return redirect()->to('/parent/payment_success');
    }

    public function messageParent()
    {
        return view('/parent/parent_message');
    }

    public function successMessage()
    {
        // Load the success message view
        return view('/parent/success_message');
    }

    public function addSecondParent()
    {
        return view('/parent/add_second_parent');
    }

    public function createUserFromParentOrAuthorizedUser($userData, $relationToStudent)
    {
        $schoolIdService = new SchoolIdService();

        // Step 1: Generate a secure token for email verification
        $token = bin2hex(random_bytes(48));
        $tokenHash = hash('sha256', $token);

        // Step 2: Determine user type based on relationship
        $userType = in_array(strtolower($relationToStudent), ['wife', 'husband']) ? 'Secondary' : 'Tertiary';

        // Step 3: Validate incoming user data
        $validation = \Config\Services::validation();
        $validation->setRules([
            'firstname' => [
                'label' => 'First Name',
                'rules' => 'required|min_length[2]|max_length[100]|regex_match[/^[A-Za-z\s\-]+$/]',
                'errors' => [
                    'regex_match' => 'First name may only contain letters, spaces, and dashes.'
                ]
            ],
            'lastname' => [
                'label' => 'Last Name',
                'rules' => 'required|min_length[2]|max_length[100]|regex_match[/^[A-Za-z\s\-]+$/]',
                'errors' => [
                    'regex_match' => 'Last name may only contain letters, spaces, and dashes.'
                ]
            ],
            'email' => [
                'label' => 'Email Address',
                'rules' => 'required|valid_email|max_length[150]|is_unique[users.email]',
                'errors' => [
                    'is_unique' => 'This email is already registered.'
                ]
            ],
            'cellphone' => [
                'label' => 'Cell Phone',
                'rules' => 'required|regex_match[/^\d{10}$/]',
                'errors' => [
                    'regex_match' => 'Phone number must be exactly 10 digits.'
                ]
            ],
            'gender' => 'required|in_list[Male,Female]',
            'city' => 'required|max_length[100]',
            'state' => 'required|max_length[100]',
            'zip' => 'required|regex_match[/^\d{5}$/]',
        ]);

        if (!$validation->run($userData)) {
            log_message('error', 'User creation failed due to invalid data: ' . json_encode($validation->getErrors()));
            return false;
        }

        // Step 4: Prepare sanitized user data
        $userEntry = [
            'firstname' => ucfirst(strtolower($userData['firstname'])),
            'lastname' => ucfirst(strtolower($userData['lastname'])),
            'gender' => $userData['gender'],
            'cellphone' => $userData['cellphone'],
            'email' => strtolower($userData['email']),
            'address_street' => $userData['address_street'] ?? '',
            'apt' => $userData['apt'] ?? null,
            'city' => ucfirst(strtolower($userData['city'])),
            'state' => strtoupper($userData['state']),
            'zip' => $userData['zip'],
            'accept_school_policy' => $userData['accept_school_policy'] ?? 0,
            'token' => $tokenHash,
            'is_verified' => 0,
            'status' => 'Inactive',
            'user_type' => $userType,
            'semester' => $this->semester,
            'school_year' => $this->schoolYear,
            'school_id' => $schoolIdService->generateUserSchoolId(),
        ];

        try {
            // Step 5: Insert user into database
            if (!$this->userModel->insert($userEntry)) {
                log_message('error', 'Failed to insert user: ' . print_r($this->userModel->errors(), true));
                return false;
            }

            $userId = $this->userModel->getInsertID();

            // Step 6: Send activation email
            $this->sendActivationEmail($userData['email'], $token);

            log_message('info', "User with ID $userId created successfully and activation email sent.");
            return $userId;
        } catch (Exception $e) {
            log_message('error', 'Exception during user creation: ' . $e->getMessage());
            return false;
        }
    }

    private function sendActivationEmail($email, $token)
    {
        $emailController = new \App\Controllers\View\EmailController();

        $subject = 'Activate Your Account';
        $activationLink = site_url('/user/confirm/' . $token);
        $message = "Please click the following link to confirm your email and set your password: $activationLink";

        if ($emailController->sendEmail($email, $subject, $message)) {
            log_message('info', 'Activation email sent successfully to ' . $email);
        } else {
            log_message('error', 'Failed to send activation email to ' . $email);
        }
    }

    /**
     * Updates or inserts an authorized user record based on user ID and email.
     *
     * @param int   $userId  The ID of the parent/user who is authorizing access.
     * @param array $data    Contains 'email', 'name', and any other required fields.
     */
    protected function updateAuthorizedUsers($userId, $data)
    {
        $validation = \Config\Services::validation();

        // Step 1: Define validation rules for authorized user fields
        $validation->setRules([
            'email' => [
                'label' => 'Email',
                'rules' => 'required|valid_email|max_length[150]',
                'errors' => [
                    'required' => 'Email is required.',
                    'valid_email' => 'Please provide a valid email address.',
                    'max_length' => 'Email must be less than 150 characters.'
                ]
            ],
            'name' => [
                'label' => 'Name',
                'rules' => 'required|min_length[3]|max_length[100]|regex_match[/^[A-Za-z\s\-]+$/]',
                'errors' => [
                    'required' => 'Name is required.',
                    'regex_match' => 'Name can only contain letters, spaces, and dashes.',
                    'min_length' => 'Name must be at least 3 characters long.',
                    'max_length' => 'Name must be less than 100 characters.'
                ]
            ]
        ]);

        // Step 2: Run validation
        if (!$validation->run($data)) {
            log_message('error', 'Invalid authorized user data: ' . json_encode($validation->getErrors()));
            return; // Skip update/insert if validation fails
        }

        // Step 3: Check if an authorized user with same email/user ID exists
        $existingAuthorizedUser = $this->authorizedUsersModel
            ->where('user_id', $userId)
            ->where('email', $data['email'])
            ->first();

        if ($existingAuthorizedUser) {
            // Step 4: Update existing authorized user
            $this->authorizedUsersModel->update($existingAuthorizedUser['id'], $data);
        } else {
            // Step 5: Insert new authorized user with pending status
            $data['user_id'] = $userId;
            $data['status'] = 'Pending'; // default status for new records
            $this->authorizedUsersModel->insert($data);
        }
    }

    public function profile($id)
    {
        // Fetch the user's data based on the given ID
        $user = $this->userModel->find($id);

        // Check if user exists
        if (!$user) {
            return redirect()->to('/')->with('error', 'User not found');
        }

        // Load the profile view with the user's data
        return view('/profile', ['user' => $user]);
    }

    public function updateProfile($id)
    {
        $user = $this->userModel->find($id);

        // Step 1: Check if user exists
        if (!$user) {
            return redirect()->to('/')->with('error', 'User not found');
        }

        // Step 2: Define validation rules
        $validation = \Config\Services::validation();
        $validation->setRules([
            'firstname' => [
                'label' => 'First Name',
                'rules' => 'required|min_length[3]|max_length[100]|regex_match[/^[A-Za-z\s\-]+$/]',
                'errors' => [
                    'regex_match' => 'First name must only contain letters, spaces, and dashes.'
                ]
            ],
            'lastname' => [
                'label' => 'Last Name',
                'rules' => 'required|min_length[3]|max_length[100]|regex_match[/^[A-Za-z\s\-]+$/]',
                'errors' => [
                    'regex_match' => 'Last name must only contain letters, spaces, and dashes.'
                ]
            ],
            'cellphone' => [
                'label' => 'Cell Phone',
                'rules' => 'required|regex_match[/^\d{10}$/]',
                'errors' => [
                    'regex_match' => 'Cell phone must be exactly 10 digits.'
                ]
            ],
            'address_street' => [
                'label' => 'Street Address',
                'rules' => 'required|min_length[5]|max_length[255]'
            ],
            'city' => [
                'label' => 'City',
                'rules' => 'required|min_length[2]|max_length[100]'
            ],
            'state' => [
                'label' => 'State',
                'rules' => 'required|min_length[2]|max_length[100]'
            ],
            'zip' => [
                'label' => 'ZIP Code',
                'rules' => 'required|regex_match[/^\d{5}$/]',
                'errors' => [
                    'regex_match' => 'ZIP code must be exactly 5 digits.'
                ]
            ]
        ]);

        // Step 3: Validate input; reload view with error messages if validation fails
        if (!$this->validate($validation->getRules())) {
            return view('/profile', [
                'user' => $user,
                'validation' => $this->validator,
            ]);
        }

        // Step 4: Prepare cleaned and validated data for update
        $updatedData = [
            'firstname'      => ucfirst(strtolower($this->request->getPost('firstname'))),
            'lastname'       => ucfirst(strtolower($this->request->getPost('lastname'))),
            'cellphone'      => $this->request->getPost('cellphone'),
            'address_street' => $this->request->getPost('address_street'),
            'city'           => ucfirst(strtolower($this->request->getPost('city'))),
            'state'          => strtoupper($this->request->getPost('state')),
            'zip'            => $this->request->getPost('zip'),
        ];

        // Step 5: Update user profile in the database
        $this->userModel->update($id, $updatedData);

        // Step 6: Redirect with success message
        session()->setFlashdata('success', 'Profile updated successfully');
        return redirect()->to('/profile/' . $id);
    }

    public function isEmailUnique(string $email): bool
    {
        $tablesAndColumns = [
            'users'             => 'email',
            'emergency_contacts' => 'email',
        ];

        foreach ($tablesAndColumns as $table => $column) {
            // Skip duplicate table lookups with different columns
            $builder = $this->db->table($table);
            $builder->where($column, $email);
            $exists = $builder->countAllResults();

            if ($exists > 0) {
                return false; // Email already exists
            }
        }

        return true; // Email is unique
    }


    //form to save student and emergency contact data
    public function saveStudentRegistration()
    {
        log_message('debug', print_r($this->request->getPost(), true));

        $schoolIdService = new SchoolIdService();
        $parentId = session()->get('user_id');
        $schoolYearContext = $this->resolveSchoolYearContext();
        $this->assertSchoolYearWritable($schoolYearContext);
        $selectedSchoolYear = $schoolYearContext->yearName();
        $this->schoolYear = $selectedSchoolYear;

        if (!$this->lastDayOfRegistration || !strtotime($this->lastDayOfRegistration)) {
            throw new \Exception('Invalid enrollment deadline date.');
        }

        if (strtolower($this->request->getMethod()) !== 'post') {
            return redirect()->back()->with('error', 'Invalid request method.');
        }

        try {
            // ✅ 1. Get existing data
            $data = $this->getRegistrationData($parentId);
            $existingKids = $data['existingKids'];
            $existingECs  = $data['emergencies'];
            $maxChilds    = $data['maxChilds'];
            $maxEmergency = $data['maxEmergency'];

            $existingKidsCount = count($existingKids);
            $existingECCount   = count($existingECs);

            $incomingFirstNames   = $this->request->getPost('studentFirstName') ?? [];
            $incomingLastNames    = $this->request->getPost('studentLastName') ?? [];
            $incomingDOBs         = $this->request->getPost('dob') ?? [];

            $newStudentCount = count(array_filter($incomingFirstNames));

            // ✅ 2. Duplicate student check against existing records
            foreach ($incomingFirstNames as $i => $firstName) {
                $lastName = trim($incomingLastNames[$i] ?? '');
                $dob      = trim($incomingDOBs[$i] ?? '');

                if (empty($firstName) || empty($lastName) || empty($dob)) continue;

                foreach ($existingKids as $kid) {
                    if (
                        strtolower($kid['firstname']) === strtolower($firstName) &&
                        strtolower($kid['lastname']) === strtolower($lastName) &&
                        $kid['dob'] === $dob
                    ) {
                        return redirect()->back()->withInput()->with('error', "Duplicate student detected: {$firstName} {$lastName} with DOB {$dob} already exists.");
                    }
                }
            }

            // ✅ 3. Duplicate student check within incoming data
            $seenStudents = [];
            foreach ($incomingFirstNames as $i => $firstName) {
                $lastName = trim($incomingLastNames[$i] ?? '');
                $dob      = trim($incomingDOBs[$i] ?? '');

                if (empty($firstName) || empty($lastName) || empty($dob)) continue;

                $key = strtolower($firstName . '|' . $lastName . '|' . $dob);
                if (isset($seenStudents[$key])) {
                    return redirect()->back()->withInput()->with('error', "Duplicate student entry in the form: {$firstName} {$lastName} with DOB {$dob}.");
                }
                $seenStudents[$key] = true;
            }

            // ✅ 4. Emergency contact validation
            $incomingECFirst  = $this->request->getPost('emergency_firstname') ?? [];
            $incomingECLast   = $this->request->getPost('emergency_lastname') ?? [];
            $incomingECPhones = $this->request->getPost('emergency_phone') ?? [];
            $incomingECEmails = $this->request->getPost('emergency_email') ?? [];

            $newECCount = count(array_filter($incomingECFirst));

            // Against existing
            foreach ($incomingECFirst as $i => $first) {
                $last  = trim($incomingECLast[$i] ?? '');
                $phone = preg_replace('/\D/', '', $incomingECPhones[$i] ?? '');
                $email = strtolower(trim($incomingECEmails[$i] ?? ''));

                if (empty($first) || empty($last)) continue;

                foreach ($existingECs as $contact) {
                    $existingPhone = preg_replace('/\D/', '', $contact['cellphone']);
                    $existingEmail = strtolower($contact['email']);

                    if (
                        strtolower($contact['emergency_contact_name']) === strtolower(trim($first . ' ' . $last)) ||
                        ($phone && $phone === $existingPhone) ||
                        ($email && $email === $existingEmail)
                    ) {
                        return redirect()->back()->withInput()->with('error', "Duplicate emergency contact: {$first} {$last} already exists.");
                    }
                }
            }

            // Within incoming data
            $seenContacts = [];
            foreach ($incomingECFirst as $i => $first) {
                $last  = trim($incomingECLast[$i] ?? '');
                $phone = preg_replace('/\D/', '', $incomingECPhones[$i] ?? '');
                $email = strtolower(trim($incomingECEmails[$i] ?? ''));

                if (empty($first) || empty($last)) continue;

                $key = strtolower($first . '|' . $last . '|' . $phone . '|' . $email);
                if (isset($seenContacts[$key])) {
                    return redirect()->back()->withInput()->with('error', "Duplicate emergency contact entry in the form: {$first} {$last}.");
                }
                $seenContacts[$key] = true;
            }

            // ✅ 5. Check limits
            if (($existingKidsCount + $newStudentCount) > $maxChilds) {
                return redirect()->back()->withInput()->with('error', "Student limit exceeded. You have $existingKidsCount and tried to add $newStudentCount (limit: $maxChilds).");
            }

            if (($existingECCount + $newECCount) > $maxEmergency) {
                return redirect()->back()->withInput()->with('error', "Emergency contact limit exceeded. You have $existingECCount and tried to add $newECCount (limit: $maxEmergency).");
            }

            // ✅ 6. Start transaction and save
            $this->db->transStart();

            $studentAdded = false;

            // Gather all POST data for last year flags
            $rawPost = $this->request->getPost();

            // Loop through students
            foreach ($incomingFirstNames as $idx => $firstName) {
                if (!empty($firstName)) {
                    // ✅ 7. Extract is_new flag (convert to boolean)
                    $lastYearKey = 'last_year_' . $idx;
                    $lastYearVal = strtolower(trim($rawPost[$lastYearKey] ?? ''));

                    $isNew = match ($lastYearVal) {
                        'yes' => false,  // was with us last year => not new
                        'no'  => true,   // was not with us => new student
                        default => null  // missing or invalid
                    };

                    if (!isset($isNew)) {
                        return redirect()->back()->withInput()->with('error', "Please select if student #" . ($idx + 1) . " was with us last year.");
                    }

                    // ✅ 8. Pass $isNew to the student save function
                    $result = $this->validateAndSaveOrUpdateStudent($idx, $parentId, $this->semester, $this->schoolYear, $schoolIdService, $isNew, null, null);

                    if ($result) {
                        $studentAdded = true;
                    }
                }
            }

            $hasEC = !empty($incomingECFirst[0]) || !empty(($this->request->getPost('emergency_lastname')[0] ?? ''));

            if ($hasEC) {
                $this->saveEmergencyContact($parentId, $this->semester, $this->schoolYear);
            }

            if (!$studentAdded && !$hasEC) {
                throw new \Exception('Please fill in at least a form before submitting.');
            }

            $this->db->transComplete();

            if ($this->db->transStatus() === false) {
                throw new \Exception('DB transaction failed.');
            }

            return redirect()->to(base_url('/parent/child_register'))->with('success', 'Registration successful!');
        } catch (\Throwable $e) {
            log_message('error', $e->getMessage());
            $this->db->transRollback();
            return redirect()->back()->withInput()->with('error', $e->getMessage());
        }
    }


    // Function to check if the parent has registered kids and redirect accordingly
    public function registerKidCheck()
    {
        $parentId = session()->get('user_id');

        if (!$parentId) {
            return redirect()->back()->with('error', 'Session expired. Please log in again.');
        }

        try {
            $data = $this->getRegistrationData($parentId);
        } catch (\RuntimeException $e) {
            return redirect()->back()->with('error', $e->getMessage());
        } catch (\Throwable $e) {
            log_message('error', "Failed to fetch registration data: " . $e->getMessage());
            return redirect()->back()->with('error', 'Failed to retrieve registration info.');
        }
        $data['lastDayOfRegistration'] = $this->lastDayOfRegistration;
        $data['registrationAgeDeadline'] = $this->dateAgeReference;
        return view('parent/register_student', $data);
    }

    private function getEnrollmentsByParent($parentId, $schoolYear)
    {
        return $this->enrollmentModel
            ->where('parent_id', $parentId)
            ->where('school_year', $schoolYear)
            ->orderBy('enrollment_date', 'DESC')
            ->findAll();
    }

    private function getRegistrationData(int $parentId): array
    {
        $schoolYearContext = $this->resolveSchoolYearContext();
        $selectedSchoolYear = $schoolYearContext->yearName();
        $enrollments     = $this->getEnrollmentsByParent($parentId, $selectedSchoolYear);

        $enrollmentMap = [];
        if (!empty($enrollments)) {
            foreach ($enrollments as $enroll) {
                $enrollmentMap[$enroll['student_id']] = $enroll;
            }
        }

        $user = $this->userModel->find($parentId);
        if (!$user || $user['user_type'] !== 'primary') {
            throw new \RuntimeException('Only primary parents are allowed to register children.');
        }

        $kids = $this->studentModel->where('parent_id', $parentId)->findAll();
        foreach ($kids as &$kid) {
            $kid['allergies'] = $this->allergyModel
                ->where('student_id', $kid['id'])
                ->findColumn('allergy') ?? [];

            $kid['medical_conditions'] = $this->medicalConditionModel
                ->where('student_id', $kid['id'])
                ->findColumn('condition_name') ?? [];

            $kid['enrollment'] = isset($enrollmentMap[$kid['id']]['id']) && !empty($enrollmentMap[$kid['id']]['id']) ? 1 : 0;
        }

        $emergencies = $this->emergencyContactModel->where('parent_id', $parentId)->findAll();

        return [
            'existingKids'   => $kids,
            'emergencies'    => $emergencies,
            'parent'         => $user,
            'maxChilds'      => $this->maxChilds,
            'maxEmergency'   => $this->maxEmergency,
            'enrollments'    => $enrollments,
            'selectedYear'   => $selectedSchoolYear,
            'isEditable'     => ! $schoolYearContext->isReadonly(),
        ];
    }

    private function validateAndSaveOrUpdateStudent($idx, $parentId, $semester, $schoolYear, $schoolIdService, $isNew = null, $studentId = null)
    {
        $firstName = $this->request->getPost('studentFirstName')[$idx] ?? null;
        $lastName  = $this->request->getPost('studentLastName')[$idx] ?? null;
        $dob       = $this->request->getPost('dob')[$idx] ?? null;
        $gender    = $this->request->getPost('gender')[$idx] ?? null;
        $grade     = $this->request->getPost('registration_grade')[$idx] ?? null;
        $conditions = $this->request->getPost('medical_conditions')[$idx] ?? [];
        $allergies  = $this->request->getPost('allergies')[$idx] ?? [];
        $photoRaw   = $this->request->getPost('photo_consent')[$idx] ?? '';

        if (!$firstName || !$lastName || !$dob || !$gender || !$grade) {
            return false;
        }

        // Normalize names: trim, collapse internal spaces, ucfirst each word
        $normalize = function (string $s): string {
            $s = trim($s);
            $s = preg_replace('/\s+/', ' ', $s);                 // collapse whitespace
            // Keep your existing formatName if you prefer; this is a safe default:
            return mb_convert_case($s, MB_CASE_TITLE, 'UTF-8');  // "ahmed ali" -> "Ahmed Ali"
        };

        $firstName = $normalize($firstName);
        $lastName  = $normalize($lastName);

        // Still do your stricter name validation if desired
        $this->validateNames($firstName);
        $this->validateNames($lastName);

        $dobObj = new \DateTime($dob);
        $objregistrationAgeDeadline = new \DateTime($this->ageDateRefernce);
        $age = $dobObj->diff($objregistrationAgeDeadline)->y;

        $validation = $this->validateDobAge($dob, $this->ageDateRefernce);
        if (!$validation['isValid']) {
            $displayDeadline = (new DateTime($this->ageDateRefernce))->format('m-d-Y');
            session()->setFlashdata(
                'error',
                "Student '{$firstName} {$lastName}' {$validation['message']}. Age at deadline would be: $displayDeadline."
            );
            return false;
        }

        $photoConsent = strtolower($photoRaw) === 'yes' ? 1 : 0;

        $studentData = [
            'firstname'            => $firstName,
            'lastname'             => $lastName,
            'age'                  => $age,
            'dob'                  => $dobObj->format('Y-m-d'),
            'gender'               => $gender,
            'registration_grade'   => $grade,
            'photo_consent'        => $photoConsent,
            'parent_id'            => (int) $parentId,
            'year_of_registration' => date('Y'),
            'school_year'          => $schoolYear,
            'semester'             => $semester,
        ];

        if (!is_null($isNew)) {
            $studentData['is_new'] = $isNew ? 1 : 0;
        }

        // ---------- DUPLICATE CHECK (case/space-insensitive) ----------
        // Using LOWER(TRIM(...)) bound safely; CI4 will bind the value.
        $fnKey = mb_strtolower(trim(preg_replace('/\s+/', ' ', $firstName)), 'UTF-8');
        $lnKey = mb_strtolower(trim(preg_replace('/\s+/', ' ', $lastName)), 'UTF-8');
        $dobStr = $dobObj->format('Y-m-d');

$existing = $this->studentModel
    ->where('school_year', $schoolYear)
    ->where('dob', $dobStr)
    ->where('firstname', $firstName)   // already normalized above
    ->where('lastname',  $lastName)    // already normalized above
    ->first();

        if (!$studentId && $existing) {
            session()->setFlashdata(
                'error',
                "Student '{$firstName} {$lastName}' with the same birthdate is already registered for $schoolYear."
            );
            return false;
        }


        // ---------- UPDATE OR INSERT ----------
        if ($studentId) {
            $this->studentModel->update($studentId, $studentData);
        } else {
            $studentData['registration_date'] = utc_now();
            $studentData['tuition_paid']      = 0;
            $studentData['school_id']         = $schoolIdService->generateStudentSchoolId();

            try {
                $studentId = $this->studentModel->insert($studentData, true);
            } catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) {
                // If DB unique index (below) triggers 1062 duplicate-key, show a friendly message
                if (strpos($e->getMessage(), '1062') !== false) {
                    session()->setFlashdata('error', "Student '{$firstName} {$lastName}' with the same birthdate is already registered for $schoolYear.");
                    return false;
                }
                throw $e; // other DB errors bubble up
            }
        }

        // ---------- SAVE MEDICAL CONDITIONS ----------
        $this->medicalConditionModel->where('student_id', $studentId)->delete();
        foreach ((array) $conditions as $c) {
            $c = trim($c);
            if ($c !== '') {
                $this->medicalConditionModel->insert([
                    'student_id'     => $studentId,
                    'condition_name' => $c,
                ]);
            }
        }

        // ---------- SAVE ALLERGIES ----------
        $this->allergyModel->where('student_id', $studentId)->delete();
        foreach ((array) $allergies as $a) {
            $a = trim($a);
            if ($a !== '') {
                $this->allergyModel->insert([
                    'student_id' => $studentId,
                    'allergy'    => $a,
                ]);
            }
        }

        return true;
    }



    /**
     * Validates if a date of birth falls within required age range by a deadline
     * 
     * @param string $dob Date of birth (YYYY-MM-DD format)
     * @param string $registrationAgeDeadline Deadline date (YYYY-MM-DD format)
     * @param int $minAge Minimum required age (default: 5)
     * @param int $maxAge Maximum allowed age (default: 18)
     * @return array ['isValid' => bool, 'message' => string]
     */
    function validateDobAge(string $dob, string $registrationAgeDeadline, int $minAge = 5, int $maxAge = 18): array
    {
        $response = ['isValid' => false, 'message' => '', 'age' => null];

        // -- Use a stable timezone for all comparisons (UTC is safest)
        $tz = new DateTimeZone('UTC');

        // 1) Empty?
        $dob = trim($dob);
        if ($dob === '') {
            $response['message'] = 'Date of birth is required';
            return $response;
        }

        // 2) Strict-parse DOB as a date-only at 00:00:00 (use leading "!" to zero unspecified fields)
        $birthDate = DateTimeImmutable::createFromFormat('!Y-m-d', $dob, $tz);
        $errs = DateTimeImmutable::getLastErrors();
        if ($birthDate === false || ($errs['warning_count'] ?? 0) > 0 || ($errs['error_count'] ?? 0) > 0) {
            $response['message'] = 'Invalid date format (Use YYYY-MM-DD)';
            return $response;
        }

        // 3) Parse deadline; if it's date-only, we set it to end-of-day to be inclusive
        try {
            $deadline = new DateTimeImmutable($registrationAgeDeadline, $tz);
        } catch (Throwable $e) {
            $deadline = new DateTimeImmutable('now', $tz);
        }
        // Inclusive of the deadline date
        $deadline = $deadline->setTime(23, 59, 59);

        // 4) Age at deadline (now that times are normalized)
        $ageAtDeadline = $birthDate->diff($deadline)->y;
        $response['age'] = $ageAtDeadline;

        // 5) Allowed birthdate window (inclusive)
        //    - Earliest birthdate = deadline - maxAge years  (exactly maxAge on deadline is OK)
        //    - Latest   birthdate = deadline - minAge years  (exactly minAge on deadline is OK)
        $minBirthDate = $deadline->modify("-{$maxAge} years")->setTime(0, 0, 0);   // earliest allowed
        $maxBirthDate = $deadline->modify("-{$minAge} years")->setTime(23, 59, 59); // latest allowed

        // 6) Validate
        $isValid = ($birthDate >= $minBirthDate) && ($birthDate <= $maxBirthDate);
        $response['isValid'] = $isValid;

        if (!$isValid) {
            $response['message'] = sprintf(
                'Must be %d-%d years old by %s. Current age would be: %d',
                $minAge,
                $maxAge,
                $deadline->format('m-d-Y'),
                $ageAtDeadline
            );
        }

        return $response;
    }


    private function validateNames($name)
    {
        if (!preg_match('/^[A-Za-z\s\-]{2,30}$/', $name)) {
            throw new InvalidArgumentException("Invalid name format: Only letters, spaces, or dashes (2–30 chars) allowed.");
        }
    }

    private function saveEmergencyContact($parentId, $semester, $schoolYear, $single = null, $id = null)
    {
        $phoneFormatter = new PhoneFormatterService(); // Load the formatter service

        // ✅ SINGLE CONTACT MODE
        if ($single !== null) {
            $firstName = $this->formatName($single['first_name'] ?? '');
            $lastName  = $this->formatName($single['last_name'] ?? '');
            $relation  = trim($single['relation'] ?? '');
            $phone     = $phoneFormatter->formatPhoneNumber($single['cellphone'] ?? '');
            $email     = strtolower(trim($single['email'] ?? ''));

            if ($firstName === '' && $lastName === '' && $phone === '(000)-000-0000' && $email === '' && $relation === '') {
                return;
            }

            $this->validateNames($firstName);
            $this->validateNames($lastName);

            if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
                throw new \Exception('Invalid email format for emergency contact.');
            }

            $fullName = $firstName . ' ' . $lastName;

            $data = [
                'parent_id'              => $parentId,
                'emergency_contact_name' => $fullName,
                'cellphone'              => $phone,
                'email'                  => $email,
                'relation'               => $relation,
                'updated_at'             => utc_now(),
            ];

            if ($id !== null) {
                $duplicate = $this->emergencyContactModel
                    ->where('parent_id', $parentId)
                    ->where('emergency_contact_name', $fullName)
                    ->where('cellphone', $phone)
                    ->where('email', $email)
                    ->where('relation', $relation)
                    ->where('id !=', $id)
                    ->first();

                if ($duplicate) {
                    session()->setFlashdata('error', 'Another emergency contact with the same information already exists.');
                    return redirect()->back()->withInput();
                }

                $this->emergencyContactModel->update($id, $data);
            } else {
                $exists = $this->emergencyContactModel->where([
                    'parent_id'              => $parentId,
                    'emergency_contact_name' => $fullName,
                    'cellphone'              => $phone,
                    'email'                  => $email,
                    'relation'               => $relation,
                ])->first();

                if ($exists) {
                    session()->setFlashdata('error', 'This emergency contact is already registered.');
                    return redirect()->back()->withInput();
                }

                $this->emergencyContactModel->insert($data);
            }
        }

        // ✅ BULK MODE
        $firstNames = $this->request->getPost('emergency_firstname') ?? [];
        $lastNames  = $this->request->getPost('emergency_lastname') ?? [];
        $relations  = $this->request->getPost('emergency_relation') ?? [];
        $phones     = $this->request->getPost('emergency_phone') ?? [];
        $emails     = $this->request->getPost('emergency_email') ?? [];

        foreach ($firstNames as $idx => $first) {
            $firstName = $this->formatName($first ?? '');
            $lastName  = $this->formatName($lastNames[$idx] ?? '');
            $relation  = trim($relations[$idx]  ?? '');
            $phone     = $phoneFormatter->formatPhoneNumber($phones[$idx] ?? '');
            $email     = strtolower(trim($emails[$idx] ?? ''));

            if ($firstName === '' && $lastName === '' && $phone === '(000)-000-0000' && $email === '' && $relation === '') {
                continue;
            }

            if ($phone === '(000)-000-0000') {
                throw new \Exception('Invalid phone number.');
            }
            if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
                throw new \Exception('Invalid email format for emergency contact.');
            }

            $fullName = $firstName . ' ' . $lastName;

            $exists = $this->emergencyContactModel->where([
                'parent_id'              => $parentId,
                'emergency_contact_name' => $fullName,
                'cellphone'              => $phone,
                'email'                  => $email,
                'relation'               => $relation,
            ])->first();

            if (!$exists) {
                $this->emergencyContactModel->insert([
                    'parent_id'              => $parentId,
                    'emergency_contact_name' => $fullName,
                    'cellphone'              => $phone,
                    'email'                  => $email,
                    'relation'               => $relation,
                ]);
            }
        }
    }

    public function editEmergencyContact($id = null)
    {
        $schoolYearContext = $this->resolveSchoolYearContext();
        $this->assertSchoolYearWritable($schoolYearContext);

        if ($id === null) {
            $parentId = session()->get('user_id');
            $contacts = $this->emergencyContactModel->where('parent_id', $parentId)->findAll();
            return view('/parent/edit_emergency_contact', ['contacts' => $contacts]);
        }

        if (strtolower($this->request->getMethod()) === 'post') {
            $rules = [
                'emergency_first_name' => [
                    'label' => 'First Name',
                    'rules' => 'required|min_length[2]|max_length[50]|regex_match[/^[-A-Za-z ]+$/]',
                    'errors' => [
                        'regex_match' => 'First name may only contain letters, spaces, and dashes.'
                    ]
                ],
                'emergency_last_name' => [
                    'label' => 'Last Name',
                    'rules' => 'required|min_length[2]|max_length[50]|regex_match[/^[-A-Za-z ]+$/]',
                    'errors' => [
                        'regex_match' => 'Last name may only contain letters, spaces, and dashes.'
                    ]
                ],
                'cellphone' => [
                    'label' => 'Cell Phone',
                    'rules' => 'required|regex_match[/^\(?\d{3}\)?[-\s]?\d{3}[-\s]?\d{4}$/]',
                    'errors' => [
                        'regex_match' => 'Please enter a valid phone number (e.g., 123-456-7890 or (123) 456 7890).'
                    ]
                ],

                'relation' => [
                    'label' => 'Relation',
                    'rules' => 'required|min_length[3]|max_length[50]'
                ],
                'email' => [
                    'label' => 'Email',
                    'rules' => 'permit_empty|valid_email|max_length[150]'
                ]
            ];

            if (!$this->validate($rules)) {
                return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
            }

            $parentId = session()->get('user_id');
            $semester = $this->semester;
            $schoolYear = $schoolYearContext->yearName();

            $this->saveEmergencyContact($parentId, $semester, $schoolYear, [
                'first_name' => $this->request->getPost('emergency_first_name'),
                'last_name'  => $this->request->getPost('emergency_last_name'),
                'cellphone'  => $this->request->getPost('cellphone'),
                'email'      => $this->request->getPost('email'),
                'relation'   => $this->request->getPost('relation'),
            ], $id);

            return redirect()->to('/parent/child_register')
                ->with('message', 'Emergency contact updated successfully.');
        }
    }

    public function editStudent($id)
    {
        $schoolYearContext = $this->resolveSchoolYearContext();
        $this->assertSchoolYearWritable($schoolYearContext);
        $this->schoolYear = $schoolYearContext->yearName();
        $schoolIdService  = new \App\Services\SchoolIdService();

        if (strtolower($this->request->getMethod()) !== 'post') {
            return redirect()->back()->with('error', 'Invalid request.');
        }

        $rules = [
            'firstname' => [
                'label' => 'First Name',
                'rules' => 'required|min_length[2]|max_length[255]|regex_match[/^[A-Za-z\s\-]+$/]',
                'errors' => ['regex_match' => 'Only letters, spaces, and dashes allowed.']
            ],
            'lastname' => [
                'label' => 'Last Name',
                'rules' => 'required|min_length[2]|max_length[255]|regex_match[/^[A-Za-z\s\-]+$/]',
                'errors' => ['regex_match' => 'Only letters, spaces, and dashes allowed.']
            ],
            'dob'                => 'required|valid_date',
            'gender'             => 'required|in_list[Male,Female]',
            'photo_consent'      => 'required|in_list[0,1]',
            'registration_grade' => 'permit_empty|max_length[50]',
        ];

        // ✅ Validate first, keep scalar post data for proper error messages
        if (!$this->validate($rules)) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $parentId   = session()->get('user_id');

        // Prepare formData only AFTER validation
        $formData = [
            'studentFirstName'     => [$this->request->getPost('firstname')],
            'studentLastName'      => [$this->request->getPost('lastname')],
            'dob'                  => [$this->request->getPost('dob')],
            'gender'               => [$this->request->getPost('gender')],
            'registration_grade'   => [$this->request->getPost('registration_grade')],
            'photo_consent'        => [$this->request->getPost('photo_consent')],
            'allergies'            => [explode(',', $this->request->getPost('allergies') ?? '')],
            'medical_conditions'   => [explode(',', $this->request->getPost('medical_conditions') ?? '')],
        ];

        // Inject temporarily only for update logic, not before validation
        $this->request->setGlobal('post', $formData);

        // Save/update
        $this->validateAndSaveOrUpdateStudent(0, $parentId, $this->semester, $this->schoolYear, $schoolIdService, $id, null, null);

        return redirect()->to('/parent/child_register')->with('success', 'Student updated!');
    }

    public function deleteStudent($id)
    {
        $this->assertSchoolYearWritable($this->resolveSchoolYearContext());

        if (strtolower($this->request->getMethod()) !== 'post') {
            return redirect()->back()->with('error', 'Invalid request method.');
        }

        $parentId = session()->get('user_id');

        // Confirm student exists and belongs to the parent
        $student = $this->studentModel
            ->where('id', $id)
            ->where('parent_id', $parentId)
            ->first();

        if (!$student) {
            return redirect()->back()->with('error', 'Student not found or unauthorized.');
        }

        // Perform deletion
        if (!$this->studentModel->delete($id)) {
            return redirect()->back()->with('error', 'Failed to delete student.');
        }

        // ✅ Check if parent has any students left
        $remainingStudents = $this->studentModel
            ->where('parent_id', $parentId)
            ->countAllResults();

        if ($remainingStudents === 0) {

            // Delete all emergency contacts linked to this parent
            $this->emergencyContactModel->where('parent_id', $parentId)->delete();
        }

        return redirect()->to('/parent/child_register')->with('success', 'Student deleted successfully.');
    }



    private function formatName(string $name): string
    {
        $name = trim($name);
        $name = strtolower($name);
        $name = ucwords($name, ' ');
        $name = implode('-', array_map('ucfirst', explode('-', $name)));

        return $name;
    }


    public function addStudentForm()
    {
        $session = session();
        $count = $session->get('registeredStudentsCount') ?? 0;

        return view('partials/student_form', [
            'registeredStudentsCount' => $count
        ]);
    }

    private function calculateAge($dob)
    {
        try {
            $parts = explode('-', $this->schoolYear);
            $endYear = isset($parts[1]) ? trim($parts[1]) : date('Y');

            $registrationAgeDeadline = "$endYear-12-31";

            // Convert both dates into DateTime objects
            $deadlineObj = \DateTime::createFromFormat('Y-m-d', $registrationAgeDeadline);
            $dobObj = \DateTime::createFromFormat('Y-m-d', $dob);

            if (!$deadlineObj || !$dobObj) {
                throw new \Exception('Invalid date format for DOB or Enrollment_deadline.');
            }

            // For display or logging purposes (if needed)
            $deadlineForDisplay = $deadlineObj->format('Y-m-d');
            $dobForDisplay = $dobObj->format('Y-m-d');

            // Compare dates
            if ($dobObj > $deadlineObj) {
                log_message('debug', "DOB ($dobForDisplay) is after deadline ($deadlineForDisplay)");
                return -1;  // Invalid age case
            }

            // Calculate age
            $ageInterval = $dobObj->diff($deadlineObj);
            return $ageInterval->y;
        } catch (\Exception $e) {
            log_message('error', 'Invalid DOB or deadline in calculateAge(): ' . $e->getMessage());
            return -1;
        }
    }

    private function calculateAgeAsOfToday(?string $dob): ?int
    {
        $dob = trim((string) $dob);

        if ($dob === '') {
            return null;
        }

        try {
            $timezone = new \DateTimeZone((string) (config('School')->attendance['timezone'] ?? user_timezone()));
            $birthDate = \DateTimeImmutable::createFromFormat('!Y-m-d', $dob, $timezone);
            $errors = \DateTimeImmutable::getLastErrors();
            $hasParseErrors = is_array($errors)
                && (($errors['warning_count'] ?? 0) > 0 || ($errors['error_count'] ?? 0) > 0);

            if ($birthDate === false || $hasParseErrors) {
                return null;
            }

            $today = new \DateTimeImmutable('today', $timezone);

            if ($birthDate > $today) {
                return null;
            }

            return $birthDate->diff($today)->y;
        } catch (\Throwable $e) {
            log_message('warning', 'Unable to calculate current age from DOB: {message}', [
                'message' => $e->getMessage(),
            ]);

            return null;
        }
    }

    private function calculateAgeAsOfSchoolYearStartYear(?string $dob, string $schoolYear): ?int
    {
        $dob = trim((string) $dob);
        $schoolYear = trim($schoolYear);

        if ($dob === '' || ! preg_match('/^(\d{4})/', $schoolYear, $matches)) {
            return null;
        }

        try {
            $timezone = new \DateTimeZone((string) (config('School')->attendance['timezone'] ?? user_timezone()));
            $birthDate = \DateTimeImmutable::createFromFormat('!Y-m-d', $dob, $timezone);
            $errors = \DateTimeImmutable::getLastErrors();
            $hasParseErrors = is_array($errors)
                && (($errors['warning_count'] ?? 0) > 0 || ($errors['error_count'] ?? 0) > 0);

            if ($birthDate === false || $hasParseErrors) {
                return null;
            }

            $schoolYearStartYearCutoff = new \DateTimeImmutable($matches[1] . '-12-31', $timezone);

            if ($birthDate > $schoolYearStartYearCutoff) {
                return null;
            }

            return $birthDate->diff($schoolYearStartYearCutoff)->y;
        } catch (\Throwable $e) {
            log_message('warning', 'Unable to calculate school-year age from DOB: {message}', [
                'message' => $e->getMessage(),
            ]);

            return null;
        }
    }

    public function parentEventPage()
    {
        $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
        $semester = session()->get('semester');
        $parentId = session()->get('user_id');

        // Get active events
        $activeEvents = $this->eventModel->getActiveEvents($schoolYear, $semester);
        $activeEventCount = is_array($activeEvents) ? count($activeEvents) : 0;

        // Get charges (participation info)
        $chargesList = $this->chargesModel->getChargesWithEventInfo($parentId, $schoolYear, $semester);

        // Build a map: "studentId:eventId" => [ 'participation' => ..., 'date' => ... ]
        $charges = [];
        $externalParticipantsByEvent = [];
        foreach ($chargesList as $charge) {
            $studentId = $charge['student_id'] ?? null;
            $eventId = (int) ($charge['event_id'] ?? 0);

            if (!empty($studentId)) {
                $key = $studentId . ':' . $eventId;
                $charges[$key] = [
                    'participation' => $charge['participation'],
                    'date' => $charge['updated_at'] ?? $charge['created_at'], // Use updated_at if available
                ];
                continue;
            }

            $externalName = trim((string) ($charge['external_firstname'] ?? '') . ' ' . (string) ($charge['external_lastname'] ?? ''));
            if ($eventId > 0 && $externalName !== '') {
                $externalParticipantsByEvent[$eventId][] = [
                    'name'          => $externalName,
                    'note'          => (string) ($charge['external_note'] ?? ''),
                    'participation' => (string) ($charge['participation'] ?? ''),
                    'event_paid'    => !empty($charge['event_paid']),
                    'charged'       => (float) ($charge['charged'] ?? ($charge['event_amount'] ?? 0)),
                ];
            }
        }

        // Get enrolled students
        $students = $this->enrollmentModel->getEnrolledStudents($parentId, $schoolYear);

        return view('parent/event_participation', [
            'activeEvents' => $activeEvents,
            'charges' => $charges,
            'externalParticipantsByEvent' => $externalParticipantsByEvent,
            'yourStudents' => $students,
            'activeEventCount' => $activeEventCount,
        ]);
    }

    public function updateParticipation()
    {
        $participations = $this->request->getPost('participation'); // ['student_id:event_id' => 'yes'|'no']
        $parentId = session()->get('user_id');

        foreach ($participations as $key => $value) {
            [$studentId, $eventId] = explode(':', $key);

            $existing = $this->chargesModel->where([
                'parent_id' => $parentId,
                'student_id' => $studentId,
                'event_id' => $eventId,
            ])->first();

            if ($value === 'no') {
                if ($existing) {
                    $this->chargesModel->delete($existing['id']);
                }
                continue; // skip to next entry
            }

            // value is 'yes'
            if ($existing) {
                $this->chargesModel->update($existing['id'], ['participation' => $value]);
            } else {
                $event = $this->eventModel->getEvent($eventId, $this->schoolYear);

                $this->chargesModel->insert([
                    'parent_id'     => $parentId,
                    'student_id'    => $studentId,
                    'event_id'      => $eventId,
                    'participation' => $value,
                    'charged'       => $event['amount'],
                    'school_year'   => $this->schoolYear,
                    'semester'      => $this->semester,
                    'updated_by'    => $parentId
                ]);
            }
        }
        //recalculate the invoice
        $this->eventController->generateInvoice($parentId);

        return redirect()->back()->with('success', 'Participation updated');
    }
}
