<?php

namespace App\Services\Billing;

use App\Models\AdditionalCharge;
use App\Models\EventCharges;
use Illuminate\Support\Facades\Log;

class BillingTotalsService
{
    public function eventSubtotal(int $parentId, string $schoolYear): float
    {
        return round(array_sum($this->eventSubtotalsByStudent($parentId, $schoolYear)), 2);
    }

    /**
     * @return array<int, float> student_id => amount
     */
    public function eventSubtotalsByStudent(int $parentId, string $schoolYear): array
    {
        $byStudent = [];

        try {
            $events = EventCharges::getChargesWithEventInfo($parentId, $schoolYear) ?? [];
            foreach ($events as $event) {
                $amount = (float) ($event['charged'] ?? 0);
                if ($amount <= 0) {
                    continue;
                }

                $studentId = (int) ($event['student_id'] ?? 0);
                if ($studentId > 0) {
                    $byStudent[$studentId] = ($byStudent[$studentId] ?? 0.0) + $amount;
                } else {
                    $byStudent[0] = ($byStudent[0] ?? 0.0) + $amount;
                }
            }
        } catch (\Throwable $e) {
            Log::warning('Failed to sum event charges.', ['error' => $e->getMessage()]);
        }

        foreach ($byStudent as $studentId => $amount) {
            $byStudent[$studentId] = round($amount, 2);
        }

        return $byStudent;
    }

    public function additionalSubtotal(int $invoiceId, ?string $schoolYear = null): float
    {
        return $this->sumAdditionalChargeRows(
            $this->fetchAdditionalChargeRows(invoiceId: $invoiceId, schoolYear: $schoolYear)
        );
    }

    public function additionalSubtotalByParent(int $parentId, string $schoolYear): float
    {
        return $this->sumAdditionalChargeRows(
            $this->fetchAdditionalChargeRows(parentId: $parentId, schoolYear: $schoolYear)
        );
    }

    /**
     * @return array<int, array{charge_type: string, amount: float}>
     */
    private function fetchAdditionalChargeRows(
        ?int $parentId = null,
        ?int $invoiceId = null,
        ?string $schoolYear = null
    ): array {
        $rows = [];

        try {
            $builder = AdditionalCharge::query()
                ->select('charge_type', 'amount')
                ->where('status', 'applied');

            if ($parentId !== null) {
                $builder->where('parent_id', $parentId);
            }

            if ($invoiceId !== null) {
                $builder->where('invoice_id', $invoiceId);
            }

            if ($schoolYear !== null && $schoolYear !== '') {
                $builder->where('school_year', $schoolYear);
            }

            $rows = $builder->get()->map(fn ($row) => (array) $row)->all();
        } catch (\Throwable $e) {
            Log::warning('Failed to load additional charges.', ['error' => $e->getMessage()]);
        }

        return $rows;
    }

    /**
     * @param array<int, array{charge_type?: string, amount?: float|int|string}> $rows
     */
    private function sumAdditionalChargeRows(array $rows): float
    {
        $additionalSubtotal = 0.0;

        foreach ($rows as $row) {
            $amount = (float) ($row['amount'] ?? 0);
            $type = strtolower((string) ($row['charge_type'] ?? 'add'));

            if (in_array($type, ['deduct', 'previous'], true)) {
                $amount = -abs($amount);
            } else {
                $amount = abs($amount);
            }

            $additionalSubtotal += $amount;
        }

        return round($additionalSubtotal, 2);
    }
}
