<?php

namespace App\Livewire\Guardian;

use App\Domain\Attendance\Models\AttendanceRecord;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Identity\Models\Guardian;
use App\Domain\Identity\Services\GuardianResolver;
use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Enums\SessionStatus;
use App\Domain\Training\Models\TrainingSession;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('بوابة ولي الأمر')]
class GuardianDashboard extends Component
{
    use UsesBranchScope;

    public function mount(): void
    {
        $user = auth()->user();

        // Any account that speaks for at least one participant belongs in the
        // member portal, guardian row or not — an adult member has none.
        //
        // Read across branches on purpose. This route is `guardian.dashboard`,
        // which config('branch_scope.unscoped_routes') does not exempt, so for
        // a staff account that is also a parent the resolver would run scoped —
        // and GuardianResolver reads Guardian and Participant, both of which
        // carry BranchScope. A coach working out of one branch whose own child
        // trains at another would resolve to an empty family and be told this
        // page is for guardians only, instead of being sent to the portal that
        // has their child in it. Whom an account speaks for is a membership
        // question, not a branch one. The resolver memoises per request, so
        // render() below reads the same unscoped answer.
        $speaksFor = $this->acrossBranches(
            fn () => app(GuardianResolver::class)->participantIdsFor($user)
        );

        if ($speaksFor !== []) {
            $this->redirect(route('parent.home'), navigate: true);
            return;
        }

        abort(403, __('هذه الصفحة مخصصة لأولياء الأمور فقط'));
    }

    public function render()
    {
        // Membership-scoped on purpose, the same way mount() is. Every query
        // below is keyed to the participant ids GuardianResolver returned for
        // this account, so it can only ever read this one family's rows — but
        // Participant, AttendanceRecord, TrainingSession and Invoice all carry
        // BranchScope now, and `guardian.dashboard` is not in
        // config('branch_scope.unscoped_routes'). Left scoped, a parent whose
        // child trains at another branch would get an empty dashboard rather
        // than their own child's. The opt-out covers the whole block because
        // that is exactly one family's data.
        //
        // mount() redirects to the portal or aborts before this ever runs; the
        // opt-out is written down so reviving the screen does not quietly
        // narrow a family's own records to one branch.
        return $this->acrossBranches(fn () => $this->renderForGuardian());
    }

    private function renderForGuardian()
    {
        $user = auth()->user();

        $participantIds = collect(app(GuardianResolver::class)->participantIdsFor($user));

        // Load children with their person data and active enrollments
        $children = Participant::whereIn('id', $participantIds)
            ->with(['person', 'activeEnrollments.group', 'activeEnrollments.program'])
            ->get();

        // Calculate attendance rates per child
        $attendanceData = [];
        foreach ($children as $child) {
            $totalRecords = AttendanceRecord::where('subject_type', Participant::class)
                ->where('subject_id', $child->id)
                ->whereNotIn('status', ['cancelled', 'exempt'])
                ->count();

            $positiveRecords = AttendanceRecord::where('subject_type', Participant::class)
                ->where('subject_id', $child->id)
                ->whereIn('status', ['present', 'late', 'partial'])
                ->count();

            $attendanceData[$child->id] = [
                'rate' => $totalRecords > 0 ? round(($positiveRecords / $totalRecords) * 100, 1) : null,
                'total' => $totalRecords,
                'positive' => $positiveRecords,
            ];
        }

        // Upcoming sessions for children's active groups
        $activeGroupIds = $children->flatMap(function ($child) {
            return $child->activeEnrollments->pluck('training_group_id');
        })->unique()->filter();

        $upcomingSessions = TrainingSession::whereIn('training_group_id', $activeGroupIds)
            ->where('session_date', '>=', now()->toDateString())
            ->where('status', SessionStatus::Scheduled)
            ->with('group')
            ->orderBy('session_date')
            ->orderBy('start_time')
            ->limit(10)
            ->get();

        // Invoices for children (billable = Participant)
        $invoices = Invoice::where('billable_type', Participant::class)
            ->whereIn('billable_id', $participantIds)
            ->orderByDesc('issue_date')
            ->limit(20)
            ->get();

        // Outstanding balance
        $totalOutstanding = Invoice::where('billable_type', Participant::class)
            ->whereIn('billable_id', $participantIds)
            ->whereIn('status', ['sent', 'partially_paid', 'overdue'])
            ->sum('due_amount');

        // Total paid
        $totalPaid = Invoice::where('billable_type', Participant::class)
            ->whereIn('billable_id', $participantIds)
            ->sum('paid_amount');

        return view('livewire.guardian.guardian-dashboard', [
            'children' => $children,
            'attendanceData' => $attendanceData,
            'upcomingSessions' => $upcomingSessions,
            'invoices' => $invoices,
            'totalOutstanding' => $totalOutstanding,
            'totalPaid' => $totalPaid,
        ]);
    }
}
