<?php

namespace App\Services\Admin\Finances;

use App\Models\TuitionInvoice;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class TuitionInvoiceService
{
    /**
     * @param  array<int>  $studentIds
     * @return Collection<int, array<int>> daftar academic_term_id per student_id yang sudah punya tagihan
     */
    public function getInvoicedTermIdsByStudent(array $studentIds): Collection
    {
        return TuitionInvoice::query()
            ->whereIn('student_id', $studentIds)
            ->get(['student_id', 'academic_term_id'])
            ->groupBy('student_id')
            ->map(fn ($rows) => $rows->pluck('academic_term_id')->values()->all());
    }

    public function paginated(
        int $perPage = 25,
        string $search = '',
        ?int $academicTermId = null,
        ?string $status = null,
        ?string $paymentMethod = null,
    ): LengthAwarePaginator {
        return $this->scopedQuery($search, $academicTermId)
            ->select(['id', 'student_id', 'academic_term_id', 'amount_due', 'due_date'])
            ->withSum('payments as paid_total', 'amount_paid')
            ->withCount('payments')
            ->with(['student.user.profile', 'student.department', 'academicTerm:id,name,semester,start_date,end_date'])
            ->when($status, fn ($q) => match ($status) {
                'paid' => $q->havingRaw('COALESCE(paid_total, 0) >= amount_due'),
                'partial' => $q->havingRaw('COALESCE(paid_total, 0) > 0 AND COALESCE(paid_total, 0) < amount_due'),
                'unpaid' => $q->havingRaw('COALESCE(paid_total, 0) <= 0'),
                default => $q,
            })
            ->when($paymentMethod, fn ($q) => $q->whereHas('payments', fn ($q) => $q->where('payment_method', $paymentMethod)))
            ->latest()
            ->paginate($perPage);
    }

    /**
     * @return array{unpaid_students: int, partial_students: int, paid_students: int, total_outstanding: float, total_collected: float, total_invoices: int}
     */
    public function summary(string $search = '', ?int $academicTermId = null): array
    {
        $invoices = $this->scopedQuery($search, $academicTermId)
            ->select(['id', 'student_id', 'amount_due'])
            ->withSum('payments as paid_total', 'amount_paid')
            ->get();

        $totalCollected = (float) $invoices->sum('paid_total');
        $totalOutstanding = (float) $invoices->sum(fn (TuitionInvoice $invoice) => max(0, $invoice->amount_due - ($invoice->paid_total ?? 0)));

        $studentsByStatus = fn (\Closure $matches) => $invoices->filter($matches)->pluck('student_id')->unique()->count();

        return [
            'unpaid_students' => $studentsByStatus(fn (TuitionInvoice $invoice) => (float) ($invoice->paid_total ?? 0) <= 0),
            'partial_students' => $studentsByStatus(fn (TuitionInvoice $invoice) => (float) ($invoice->paid_total ?? 0) > 0 && (float) ($invoice->paid_total ?? 0) < (float) $invoice->amount_due),
            'paid_students' => $studentsByStatus(fn (TuitionInvoice $invoice) => (float) ($invoice->paid_total ?? 0) >= (float) $invoice->amount_due),
            'total_outstanding' => $totalOutstanding,
            'total_collected' => $totalCollected,
            'total_invoices' => $invoices->count(),
        ];
    }

    private function scopedQuery(string $search = '', ?int $academicTermId = null)
    {
        return TuitionInvoice::query()
            ->when($search, fn ($q) => $q->whereHas('student', fn ($q) => $q->where('student_number', 'like', "%{$search}%")
                ->orWhereHas('user.profile', fn ($q) => $q->where('full_name', 'like', "%{$search}%"))))
            ->when($academicTermId, fn ($q) => $q->where('academic_term_id', $academicTermId));
    }

    public function createMany(array $studentIds, array $data): void
    {
        DB::transaction(function () use ($studentIds, $data) {
            foreach ($studentIds as $studentId) {
                TuitionInvoice::create([
                    'student_id' => $studentId,
                    'academic_term_id' => $data['academic_term_id'],
                    'amount_due' => $data['amount_due'],
                    'due_date' => $data['due_date'],
                ]);
            }
        });
    }

    public function update(TuitionInvoice $invoice, array $data): TuitionInvoice
    {
        $invoice->student_id = $data['student_id'];
        $invoice->academic_term_id = $data['academic_term_id'];
        $invoice->amount_due = $data['amount_due'];
        $invoice->due_date = $data['due_date'] ?? null;
        $invoice->update();

        return $invoice;
    }

    public function delete(TuitionInvoice $invoice): bool
    {
        return $invoice->delete();
    }
}
