<?php
namespace App\Services;

use App\Models\ConfigurationModel;
use App\Models\PaymentModel;
use App\Models\InvoiceModel;
use App\Models\ClassSectionModel;

class FeeCalculationService
{
    public function calculateRefund(array $students, int $parentId): float
    {
        $configModel = new ConfigurationModel();
        $paymentModel = new PaymentModel();
        $invoiceModel = new InvoiceModel();
        $classSectionModel = new ClassSectionModel();

        $schoolYear = $configModel->getConfig('school_year');
        $refundDeadline = date('Y-m-d', strtotime($configModel->getConfig('refund_deadline')));
        $weekOfStudy = (float) ($configModel->getConfig('weeks_study') ?? 8);
        $schoolEndDate = date('Y-m-d', strtotime($configModel->getConfig('last_school_day')));
        $totalPaid = $paymentModel->getTotalPaidByParentId($parentId, $schoolYear);

        if ($totalPaid <= 0) {
            log_message('info', "No payments made. Refund = 0.");
            return 0;
        }

        // Classify and enrich student data
        $registeredStudents = [];
        $withdrawnStudents = [];

        foreach ($students as &$student) {
            $gradeName = $classSectionModel->getClassSectionNameBySectionId($student['class_section_id']);
            $student['grade'] = strtoupper(trim($gradeName));

            if (in_array($student['enrollment_status'], ['withdrawn', 'refund pending', 'withdraw under review'])) {
                $withdrawnStudents[] = $student;
            } elseif (
                in_array($student['enrollment_status'], ['enrolled', 'payment pending']) &&
                $student['admission_status'] === 'accepted'
            ) {
                $registeredStudents[] = $student;
            }
        }
        unset($student);

        if (empty($withdrawnStudents)) {
            log_message('info', "No withdrawn students found. Refund = 0.");
            return 0;
        }

        // Combine all students for proper fee tiering
        $allStudents = array_merge($registeredStudents, $withdrawnStudents);

        // Sort all students by grade for correct tiering
        usort($allStudents, function ($a, $b) {
            return $this->compareGrades($a['grade'], $b['grade']);
        });

        // Retrieve fee configs
        $firstStudentFee = (float) ($configModel->getConfig('first_student_fee') ?? 350);
        $secondStudentFee = (float) ($configModel->getConfig('second_student_fee') ?? 200);
        $youthFee = (float) ($configModel->getConfig('youth_fee') ?? 180);

        // Assign tuition_fee to all students (before filtering refunds)
        $regularCount = 0;
        foreach ($allStudents as &$student) {
            $gradeLevel = $this->getGradeLevel($student['grade']);

            if ($gradeLevel > 9) {
                $studentFee = $youthFee;
            } else {
                $studentFee = ($regularCount === 0) ? $firstStudentFee : $secondStudentFee;
                $regularCount++;
            }
        }
        unset($student);

        // Calculate refund for withdrawn students
        $refundAmount = 0;

        foreach ($withdrawnStudents as $student) {
            if (empty($student['withdrawal_date'])) {
                log_message('warning', "Missing withdraw date for student ID: {$student['student_id']}");
                continue;
            }

            $withdrawDate = date('Y-m-d', strtotime($student['withdrawal_date']));
            if (strtotime($withdrawDate) > strtotime($refundDeadline)) {
                log_message('info', "Withdraw date {$withdrawDate} is after refund deadline {$refundDeadline}. No refund for this student.");
                continue;
            }

            $withdrawDateObj = new \DateTime($withdrawDate);
            $schoolEndDateObj = new \DateTime($schoolEndDate);
            $daysRemaining = $withdrawDateObj->diff($schoolEndDateObj)->days;
            $weeksRemaining = min($weekOfStudy, max(0, ceil($daysRemaining / 7)));

            //$studentFee = $student['tuition_fee'];
            $proportionalRefund = ($studentFee / $weekOfStudy) * $weeksRemaining;
            $refundAmount += $proportionalRefund;

            log_message('info', "Student ID {$student['student_id']} refund portion: {$proportionalRefund} of {$studentFee} for {$weeksRemaining} weeks.");
        }

        if ($refundAmount > $totalPaid) {
            log_message('info', "Refund capped at total paid amount: {$totalPaid}");
            return $totalPaid;
        }

        log_message('info', "Final calculated refund: {$refundAmount}");
        return $refundAmount;
    }


    private function compareGrades($gradeA, $gradeB)
    {
        $valA = $this->getGradeLevel($gradeA);
        $valB = $this->getGradeLevel($gradeB);

        if ($valA !== $valB) return $valA <=> $valB;

        // Same level, compare suffix
        preg_match('/\d+([A-Z]*)$/i', strtoupper($gradeA), $suffixA);
        preg_match('/\d+([A-Z]*)$/i', strtoupper($gradeB), $suffixB);

        return strcmp($suffixA[1] ?? '', $suffixB[1] ?? '');
    }

    private function getGradeLevel($grade)
    {
        if (strtoupper($grade) === 'K') return 0;
        if (strtoupper($grade) === 'Y') return 99;

        if (preg_match('/^(\d+)([A-Z]*)$/i', $grade, $matches)) {
            return (int) $matches[1];
        }

        return 999; // fallback for unknown/malformed grades
    }


    private function calculateTotalTuitionFee(array $students): float
    {
        $configModel = new ConfigurationModel();
        $classSectionModel = new \App\Models\ClassSectionModel();

        $firstStudentFee = (float) ($configModel->getConfig('first_student_fee') ?? 350);
        $secondStudentFee = (float) ($configModel->getConfig('second_student_fee') ?? 200);
        $youthFee = (float) ($configModel->getConfig('youth_fee') ?? 180);

        // ✅ Pre-fetch and assign grade/class section names before sorting
        foreach ($students as &$student) {
            $gradeName = $classSectionModel->getClassSectionNameBySectionId($student['class_section_id']);
            $student['grade'] = strtoupper(trim($gradeName));
        }
        unset($student); // break reference

        // ✅ Sort students by grade
        usort($students, function ($a, $b) {
            return $this->compareGrades($a['grade'], $b['grade']);
        });

        $regularCount = 0;
        $totalFee = 0;

        // ✅ Calculate fee
        foreach ($students as $student) {
            $gradeLevel = $this->getGradeLevel($student['grade']);

            if ($gradeLevel > 9) {
                $totalFee += $youthFee;
            } else {
                $totalFee += ($regularCount === 0) ? $firstStudentFee : $secondStudentFee;
                $regularCount++;
            }
        }

        return $totalFee;
    }

}
