<?php

namespace App\Services\Fees;

use App\Models\ClassSection;
use App\Models\Configuration;
use App\Models\DiscountUsage;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Student;
use App\Models\StudentClass;
use App\Services\Invoices\InvoiceGradeService;
use Illuminate\Support\Facades\Log;

class FeeRefundDetailService
{
    public function calculateTuitionSubtotalFromStudents(
        array $registeredKids,
        array $withdrawnKids,
        string $schoolYear,
        ?string $refundDeadlineOverride = null
    ): array {
        $refundDeadlineRaw = $refundDeadlineOverride ?? (string) (Configuration::getConfig('refund_deadline') ?? '');

        $deadline = null;
        $tz = null;
        try {
            $tzName = (string) (config('School')->attendance['timezone'] ?? (function_exists('user_timezone') ? user_timezone() : 'UTC'));
            $tz = new \DateTimeZone($tzName);
            $deadline = new \DateTimeImmutable($refundDeadlineRaw, $tz);
        } catch (\Throwable $e) {
            Log::warning('Refund deadline missing/invalid for tuition calc; withdrawn students will be billed.', [
                'error' => $e->getMessage(),
            ]);
        }

        $tuitionStudents = $registeredKids;
        foreach ($withdrawnKids as $kid) {
            $rawDate = $kid['withdrawal_date'] ?? null;
            if (empty($rawDate) || !$deadline) {
                Log::warning('Missing withdrawal date or deadline; billing withdrawn student.', [
                    'student_id' => $kid['student_id'] ?? null,
                ]);
                $tuitionStudents[] = $kid;
                continue;
            }

            try {
                $withdrawDate = new \DateTimeImmutable((string) $rawDate, $tz ?? new \DateTimeZone('UTC'));
            } catch (\Throwable $e) {
                Log::warning('Invalid withdrawal date; billing withdrawn student.', [
                    'student_id' => $kid['student_id'] ?? null,
                ]);
                $tuitionStudents[] = $kid;
                continue;
            }

            if ($withdrawDate->format('Y-m-d') > $deadline->format('Y-m-d')) {
                $tuitionStudents[] = $kid;
            }
        }

        $tuitionStudents = array_values(array_filter($tuitionStudents, function ($student) use ($schoolYear) {
            $sid = (int) ($student['student_id'] ?? 0);
            return $sid > 0 && StudentClass::hasNonEventAssignment($sid, $schoolYear);
        }));

        foreach ($tuitionStudents as &$student) {
            $name = null;
            if (!empty($student['class_section_id'])) {
                $name = ClassSection::getClassSectionNameBySectionId($student['class_section_id']);
            }
            $student['grade_name'] = is_string($name) ? strtoupper(trim($name)) : 'N/A';
        }
        unset($student);

        $gradeFee = (int) (Configuration::getConfig('grade_fee') ?? 9);
        $firstStudentFee = (float) (Configuration::getConfig('first_student_fee') ?? 350);
        $secondStudentFee = (float) (Configuration::getConfig('second_student_fee') ?? 200);
        $youthFee = (float) (Configuration::getConfig('youth_fee') ?? 180);

        $regularCount = 0;
        $youthCount = 0;
        $grades = new InvoiceGradeService($gradeFee);
        foreach ($tuitionStudents as $student) {
            $level = (int) ($grades->getGradeLevel($student['grade_name'] ?? '')['level'] ?? 999);
            if ($level > $gradeFee) {
                $youthCount++;
            } else {
                $regularCount++;
            }
        }

        $tuitionSubtotal = 0.0;
        $tuitionSubtotal += $youthCount * $youthFee;
        if ($regularCount >= 2) {
            $tuitionSubtotal += $firstStudentFee + ($regularCount - 1) * $secondStudentFee;
        } elseif ($regularCount === 1) {
            $tuitionSubtotal += $firstStudentFee;
        }

        return [
            'tuition_subtotal' => round($tuitionSubtotal, 2),
            'tuition_students' => $tuitionStudents,
            'regular_count' => $regularCount,
            'youth_count' => $youthCount,
        ];
    }

    public function calculateRefundDetails(array $students, int $parentId): array
    {
        $schoolYear = (string) (Configuration::getConfig('school_year') ?? '');
        if ($schoolYear === '') {
            return ['ok' => false, 'error' => 'Missing school_year configuration.'];
        }

        $refundDeadlineRaw = (string) (Configuration::getConfig('refund_deadline') ?? '');
        if ($refundDeadlineRaw === '') {
            return ['ok' => false, 'error' => 'Refund deadline is not configured.'];
        }

        try {
            $refundDeadline = (new \DateTimeImmutable($refundDeadlineRaw))->format('Y-m-d');
        } catch (\Throwable $e) {
            return ['ok' => false, 'error' => 'Refund deadline is invalid.'];
        }

        $bookPriceRaw = Configuration::getConfig('book_price');
        if ($bookPriceRaw === null || $bookPriceRaw === '') {
            return ['ok' => false, 'error' => 'Book price is not configured.'];
        }
        if (!is_numeric($bookPriceRaw)) {
            return ['ok' => false, 'error' => 'Book price must be numeric.'];
        }
        $bookPrice = round((float) $bookPriceRaw, 2);
        if ($bookPrice < 0) {
            return ['ok' => false, 'error' => 'Book price must be zero or greater.'];
        }

        $withdrawDates = [];
        $registered = [];
        $withdrawnEligible = [];
        $withdrawnLate = [];
        $hasWithdrawn = false;
        foreach ($students as $student) {
            $status = (string) ($student['enrollment_status'] ?? '');
            if (in_array($status, ['enrolled', 'payment pending'], true)) {
                $registered[] = $student;
                continue;
            }
            if (!in_array($status, ['withdrawn', 'refund pending', 'withdraw under review'], true)) {
                continue;
            }
            $hasWithdrawn = true;
            $raw = $student['withdrawal_date'] ?? null;
            if (empty($raw)) {
                return ['ok' => false, 'error' => 'Missing withdrawal date for a withdrawn student.'];
            }
            try {
                $withdrawDate = (new \DateTimeImmutable((string) $raw))->format('Y-m-d');
                $withdrawDates[] = $withdrawDate;
                if (strtotime($withdrawDate) <= strtotime($refundDeadline)) {
                    $withdrawnEligible[] = $student;
                } else {
                    $withdrawnLate[] = $student;
                }
            } catch (\Throwable $e) {
                return ['ok' => false, 'error' => 'Invalid withdrawal date for a withdrawn student.'];
            }
        }

        if (!$hasWithdrawn) {
            return ['ok' => false, 'error' => 'No withdrawn students found for refund evaluation.'];
        }

        $withdrawDateUsed = max($withdrawDates);
        if (count(array_unique($withdrawDates)) > 1) {
            Log::warning('Multiple withdrawal dates detected for refund calc; using latest date.', [
                'withdraw_date' => $withdrawDateUsed,
                'parent_id' => $parentId,
            ]);
        }

        $totalPaid = Payment::getTotalPaidByParentId($parentId, $schoolYear);
        if ($totalPaid < 0) {
            return ['ok' => false, 'error' => 'Paid amount is invalid.'];
        }
        $totalPaid = round((float) $totalPaid, 2);

        $invoiceTotal = 0.0;
        $totalDiscount = 0.0;
        $fullDiscount = false;
        $invoice = Invoice::query()
            ->where('parent_id', $parentId)
            ->where('school_year', $schoolYear)
            ->orderByDesc('created_at')
            ->first();
        if ($invoice) {
            $invoiceTotal = (float) ($invoice->total_amount ?? 0.0);
            $totalDiscount = DiscountUsage::getTotalDiscountByParentIdAndSchoolYear($parentId, $schoolYear);
            if ($invoiceTotal > 0 && $totalDiscount >= $invoiceTotal - 0.00001) {
                $fullDiscount = true;
            }
        }

        $eligible = !empty($withdrawnEligible);

        $refundAmount = 0.0;
        $debitAmount = 0.0;
        $refundableTuition = 0.0;
        $remainingCount = 0;
        if ($eligible) {
            $totalStudentCount = (int) Student::query()
                ->where('parent_id', $parentId)
                ->distinct()
                ->count('id');

            $firstStudentFee = (float) (Configuration::getConfig('first_student_fee') ?? 350);
            $secondStudentFee = (float) (Configuration::getConfig('second_student_fee') ?? 200);

            $withdrawnCount = count($withdrawnEligible);
            $remainingCount = max(0, $totalStudentCount - $withdrawnCount);
            if ($totalStudentCount >= 2 && $remainingCount >= 1) {
                $refundableTuition = round($secondStudentFee * $withdrawnCount, 2);
            } elseif ($totalStudentCount >= 2 && $remainingCount === 0) {
                $refundableTuition = round($firstStudentFee + max(0, $withdrawnCount - 1) * $secondStudentFee, 2);
            } else {
                $refundableTuition = round($firstStudentFee * $withdrawnCount, 2);
            }

            $refundableCash = min($totalPaid, $refundableTuition);
            $raw = round($refundableCash - $bookPrice, 2);
            if ($raw > 0 && !$fullDiscount) {
                $refundAmount = $raw;
            } elseif ($raw < 0 && !$fullDiscount) {
                $debitAmount = abs($raw);
            }
        }

        return [
            'ok' => true,
            'eligible' => $eligible,
            'withdraw_date' => $withdrawDateUsed,
            'refund_deadline' => $refundDeadline,
            'paid_amount' => $totalPaid,
            'book_price' => $bookPrice,
            'refund_amount' => $refundAmount,
            'debit_amount' => $debitAmount,
            'refundable_tuition' => round($refundableTuition, 2),
            'withdrawn_eligible_count' => count($withdrawnEligible),
            'total_student_count' => (int) ($totalStudentCount ?? 0),
            'remaining_count' => $remainingCount,
            'invoice_total' => round($invoiceTotal, 2),
            'discount_total' => round($totalDiscount, 2),
            'full_discount' => $fullDiscount,
        ];
    }

    public function calculateRefund(array $students, int $parentId): float
    {
        $result = $this->calculateRefundDetails($students, $parentId);
        return (float) ($result['refund_amount'] ?? 0.0);
    }

    public function getGradeLevelInfo($grade, ?int $gradeFee = null): array
    {
        $resolvedGradeFee = $gradeFee ?? (int) (Configuration::getConfig('grade_fee') ?? 9);
        $grades = new InvoiceGradeService($resolvedGradeFee);
        return $grades->getGradeLevel($grade);
    }

    public function compareGrades($gradeA, $gradeB, ?int $gradeFee = null): int
    {
        $resolvedGradeFee = $gradeFee ?? (int) (Configuration::getConfig('grade_fee') ?? 9);
        $grades = new InvoiceGradeService($resolvedGradeFee);
        return $grades->compareGrades((string) $gradeA, (string) $gradeB);
    }

    public function calculateTotalTuitionFee(
        array $students,
        ?int $gradeFee = null,
        ?float $firstStudentFee = null,
        ?float $secondStudentFee = null,
        ?float $youthFee = null
    ): float {
        $resolvedGradeFee = $gradeFee ?? (int) (Configuration::getConfig('grade_fee') ?? 9);
        $resolvedFirstFee = $firstStudentFee ?? (float) (Configuration::getConfig('first_student_fee') ?? 350);
        $resolvedSecondFee = $secondStudentFee ?? (float) (Configuration::getConfig('second_student_fee') ?? 200);
        $resolvedYouthFee = $youthFee ?? (float) (Configuration::getConfig('youth_fee') ?? 180);

        foreach ($students as &$student) {
            $gradeName = ClassSection::getClassSectionNameBySectionId($student['class_section_id'] ?? null);
            $student['grade'] = strtoupper(trim((string) $gradeName));
        }
        unset($student);

        $regularCount = 0;
        $youthCount = 0;
        $grades = new InvoiceGradeService($resolvedGradeFee);

        foreach ($students as $student) {
            $level = (int) ($grades->getGradeLevel($student['grade'] ?? '')['level'] ?? 999);
            if ($level > $resolvedGradeFee) {
                $youthCount++;
            } else {
                $regularCount++;
            }
        }

        $total = 0.0;
        $total += $youthCount * $resolvedYouthFee;

        if ($regularCount >= 2) {
            $total += $resolvedFirstFee;
            $total += ($regularCount - 1) * $resolvedSecondFee;
        } elseif ($regularCount === 1) {
            $total += $resolvedFirstFee;
        }

        return $total;
    }
}
