<?php

namespace App\Services\Billing;

use App\Models\DiscountUsage;
use App\Models\Payment;
use App\Models\Refund;
use App\Services\Fees\FeeConfigService;
use App\Services\Fees\TuitionCalculationService;
use Illuminate\Support\Facades\Log;

/**
 * BalanceCalculationService (plan §6, §13 Phase 2)
 *
 * Combines tuition, extra charges, and event charges, then nets payments,
 * credits, and refunds to produce family- and student-level balances.
 */
class BalanceCalculationService
{
    public function __construct(
        private FeeConfigService $config,
        private TuitionCalculationService $tuition,
        private BillingTotalsService $billingTotals
    ) {
    }

    /**
     * @param array<int, array<string, mixed>> $students
     *
     * @return array<string, mixed>
     */
    public function calculateFamilyAccount(array $students, int $parentId, ?string $schoolYear = null): array
    {
        $schoolYear = $schoolYear ?? $this->config->getSchoolYear();

        $tuitionBreakdown = $this->tuition->calculate($students);
        $tuitionTotal = (float) ($tuitionBreakdown['family_total'] ?? 0);
        $extraCharges = $this->billingTotals->additionalSubtotalByParent($parentId, $schoolYear);
        $eventByStudent = $this->billingTotals->eventSubtotalsByStudent($parentId, $schoolYear);
        $eventCharges = round(array_sum($eventByStudent), 2);
        $lateFees = 0.0;

        $totalCharges = round($tuitionTotal + $extraCharges + $eventCharges + $lateFees, 2);

        $totalPayments = round(Payment::getTotalPaidByParentId($parentId, $schoolYear), 2);
        $totalCredits = round(
            DiscountUsage::getTotalDiscountByParentIdAndSchoolYear($parentId, $schoolYear),
            2
        );
        $refundsApplied = round(
            Refund::totalApprovedPaidOutForParentAndYear($parentId, $schoolYear),
            2
        );

        $netting = round($totalPayments + $totalCredits + $refundsApplied, 2);
        $rawRemaining = round($totalCharges - $netting, 2);
        $remainingBalance = max(0.0, $rawRemaining);
        $accountCredit = $rawRemaining < 0 ? round(abs($rawRemaining), 2) : 0.0;

        $studentRows = $this->buildStudentBalances(
            $tuitionBreakdown['students'] ?? [],
            $eventByStudent,
            $totalCharges,
            $totalPayments,
            $totalCredits,
            $refundsApplied
        );

        Log::info('Family account balance calculated', [
            'parent_id' => $parentId,
            'school_year' => $schoolYear,
            'total_charges' => $totalCharges,
            'remaining_balance' => $remainingBalance,
            'account_credit' => $accountCredit,
        ]);

        return [
            'parent_id' => $parentId,
            'school_year' => $schoolYear,
            'charges' => [
                'tuition' => $tuitionTotal,
                'extra_charges' => $extraCharges,
                'event_charges' => $eventCharges,
                'late_fees' => $lateFees,
                'total' => $totalCharges,
            ],
            'credits' => [
                'discounts' => $totalCredits,
                'total' => $totalCredits,
            ],
            'payments' => $totalPayments,
            'refunds_applied' => $refundsApplied,
            'remaining_balance' => $remainingBalance,
            'account_credit' => $accountCredit,
            'tuition' => $tuitionBreakdown,
            'students' => $studentRows,
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $tuitionStudents
     * @param array<int, float> $eventByStudent
     *
     * @return array<int, array<string, mixed>>
     */
    private function buildStudentBalances(
        array $tuitionStudents,
        array $eventByStudent,
        float $familyTotalCharges,
        float $totalPayments,
        float $totalCredits,
        float $refundsApplied
    ): array {
        $familyNetting = $totalPayments + $totalCredits + $refundsApplied;
        $rows = [];

        foreach ($tuitionStudents as $student) {
            $studentId = (int) ($student['student_id'] ?? 0);
            $tuitionAmount = (float) ($student['final_tuition_amount'] ?? $student['tuition_fee'] ?? 0);
            $eventAmount = (float) ($eventByStudent[$studentId] ?? 0);
            $extraAmount = 0.0;
            $studentTotalCharges = round($tuitionAmount + $eventAmount + $extraAmount, 2);

            $share = $familyTotalCharges > 0
                ? ($studentTotalCharges / $familyTotalCharges)
                : 0.0;

            $paymentsApplied = round($totalPayments * $share, 2);
            $creditsApplied = round($totalCredits * $share, 2);
            $refundsAppliedShare = round($refundsApplied * $share, 2);
            $allocatedNetting = round($familyNetting * $share, 2);
            $rawRemaining = round($studentTotalCharges - $allocatedNetting, 2);

            $rows[] = [
                'student_id' => $studentId > 0 ? $studentId : ($student['student_id'] ?? null),
                'student_name' => $student['student_name'] ?? null,
                'grade' => $student['grade'] ?? null,
                'enrollment_status' => $student['enrollment_status'] ?? null,
                'tuition' => round($tuitionAmount, 2),
                'extra_charges' => $extraAmount,
                'event_charges' => $eventAmount,
                'total_charges' => $studentTotalCharges,
                'payments_applied' => $paymentsApplied,
                'credits_applied' => $creditsApplied,
                'refunds_applied' => $refundsAppliedShare,
                'remaining_balance' => max(0.0, $rawRemaining),
                'account_credit' => $rawRemaining < 0 ? round(abs($rawRemaining), 2) : 0.0,
            ];
        }

        $unassignedEvent = (float) ($eventByStudent[0] ?? 0);
        if ($unassignedEvent > 0) {
            $rows[] = [
                'student_id' => null,
                'student_name' => null,
                'grade' => null,
                'enrollment_status' => null,
                'tuition' => 0.0,
                'extra_charges' => 0.0,
                'event_charges' => $unassignedEvent,
                'total_charges' => $unassignedEvent,
                'payments_applied' => 0.0,
                'credits_applied' => 0.0,
                'refunds_applied' => 0.0,
                'remaining_balance' => $unassignedEvent,
                'account_credit' => 0.0,
            ];
        }

        return $rows;
    }
}
