<?php

namespace App\Livewire\Parent;

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\Training\Enums\EvaluationStatus;
use App\Domain\Training\Enums\SessionStatus;
use App\Domain\Training\Models\Evaluation;
use App\Domain\Training\Models\TrainingSession;
use Carbon\Carbon;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.parent')]
#[Title('الرئيسية - بوابة ولي الأمر')]
class ParentHome extends Component
{
    /**
     * Locked because render() filters attendance, invoices and evaluations on this
     * id directly. A plain public property is settable from the browser, so
     * without this the checks in mount() and selectChild() are decoration: a
     * guardian could walk participant ids and read any child's balance,
     * attendance and evaluations. Locked still allows the server-side writes below.
     */
    #[Locked]
    public ?int $activeChildId = null;

    public function mount(): void
    {
        $guardian = $this->getGuardian();
        $childrenIds = $this->getChildrenIds();

        if (empty($childrenIds)) {
            abort(403, __('لا يوجد أبناء مسجلين'));
        }

        $this->activeChildId = session('active_child_id', $childrenIds[0] ?? null);

        if (! in_array($this->activeChildId, $childrenIds)) {
            $this->activeChildId = $childrenIds[0];
        }
    }

    public function selectChild(int $childId): void
    {
        $childrenIds = $this->getChildrenIds();

        if (in_array($childId, $childrenIds)) {
            $this->activeChildId = $childId;
            session(['active_child_id' => $childId]);
        }
    }

    public function render()
    {
        $guardian = $this->getGuardian();
        $childrenIds = $this->getChildrenIds();

        // Defence in depth behind #[Locked]: every query below filters on
        // $activeChildId, so it is re-checked against this guardian's children on
        // each render rather than trusted from mount(). Children can also change
        // between requests — a withdrawal mid-session would otherwise leave a
        // stale id in scope.
        if (! in_array($this->activeChildId, $childrenIds, true)) {
            $this->activeChildId = $childrenIds[0] ?? null;
        }

        $children = Participant::whereIn('id', $childrenIds)
            ->with('person')
            ->get();

        $activeChild = $children->firstWhere('id', $this->activeChildId);

        // Time-based greeting
        $hour = now()->hour;
        $greeting = $hour < 12 ? 'صباح الخير' : 'مساء الخير';

        // Today's next session for active child
        $activeGroupIds = $activeChild
            ? $activeChild->activeEnrollments()->pluck('training_group_id')->toArray()
            : [];

        $nextSession = null;
        if (! empty($activeGroupIds)) {
            $nextSession = TrainingSession::whereIn('training_group_id', $activeGroupIds)
                ->where(function ($q) {
                    $q->where('session_date', '>', now()->toDateString())
                        ->orWhere(function ($q2) {
                            $q2->where('session_date', now()->toDateString())
                                ->where('start_time', '>=', now()->format('H:i:s'));
                        });
                })
                ->where('status', SessionStatus::Scheduled)
                ->with(['group.program', 'facility'])
                ->orderBy('session_date')
                ->orderBy('start_time')
                ->first();
        }

        // Attendance rate this month
        $monthStart = now()->startOfMonth()->toDateString();
        $monthEnd = now()->endOfMonth()->toDateString();

        $totalRecords = AttendanceRecord::where('subject_type', Participant::class)
            ->where('subject_id', $this->activeChildId)
            ->whereHas('session', fn ($q) => $q->whereBetween('session_date', [$monthStart, $monthEnd]))
            ->whereNotIn('status', ['cancelled', 'exempt'])
            ->count();

        $positiveRecords = AttendanceRecord::where('subject_type', Participant::class)
            ->where('subject_id', $this->activeChildId)
            ->whereHas('session', fn ($q) => $q->whereBetween('session_date', [$monthStart, $monthEnd]))
            ->whereIn('status', ['present', 'late', 'partial'])
            ->count();

        $attendanceRate = $totalRecords > 0 ? round(($positiveRecords / $totalRecords) * 100, 1) : null;

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

        // Last evaluation score
        $lastEvaluation = Evaluation::where('participant_id', $this->activeChildId)
            ->where('status', EvaluationStatus::Shared)
            ->orderByDesc('evaluation_date')
            ->first();

        // Activity feed (recent events)
        $recentAttendance = AttendanceRecord::where('subject_type', Participant::class)
            ->where('subject_id', $this->activeChildId)
            ->whereIn('status', ['present', 'late', 'absent', 'excused'])
            ->with('session.group')
            ->orderByDesc('marked_at')
            ->limit(5)
            ->get()
            ->map(fn ($record) => [
                'type' => 'attendance',
                'status' => $record->status->value,
                'date' => $record->marked_at ?? $record->created_at,
                'description' => __('تم تسجيل الحضور') . ': ' . ($record->status->value === 'present' ? __('حاضر') : ($record->status->value === 'late' ? __('متأخر') : ($record->status->value === 'absent' ? __('غائب') : __('معذور')))),
                'group' => $record->session?->group?->name_ar ?? '',
            ]);

        $recentInvoices = Invoice::where('billable_type', Participant::class)
            ->where('billable_id', $this->activeChildId)
            ->orderByDesc('created_at')
            ->limit(3)
            ->get()
            ->map(fn ($invoice) => [
                'type' => 'invoice',
                'status' => $invoice->status->value,
                'date' => $invoice->created_at,
                'description' => __('فاتورة جديدة') . ' #' . $invoice->number . ' - ' . number_format($invoice->total_amount / 100, 2) . ' ج.م',
                'group' => '',
            ]);

        $recentEvaluations = Evaluation::where('participant_id', $this->activeChildId)
            ->where('status', EvaluationStatus::Shared)
            ->orderByDesc('shared_at')
            ->limit(2)
            ->get()
            ->map(fn ($eval) => [
                'type' => 'evaluation',
                'status' => 'shared',
                'date' => $eval->shared_at ?? $eval->created_at,
                'description' => __('تقييم جديد') . ' - ' . __('الدرجة') . ': ' . $eval->overall_score,
                'group' => $eval->group?->name_ar ?? '',
            ]);

        $activityFeed = $recentAttendance
            ->concat($recentInvoices)
            ->concat($recentEvaluations)
            ->sortByDesc('date')
            ->take(10)
            ->values();

        return view('livewire.parent.parent-home', [
            'greeting' => $greeting,
            'children' => $children,
            'activeChild' => $activeChild,
            'nextSession' => $nextSession,
            'attendanceRate' => $attendanceRate,
            'outstandingBalance' => $outstandingBalance,
            'lastEvaluation' => $lastEvaluation,
            'activityFeed' => $activityFeed,
        ]);
    }

    private function getGuardian(): Guardian
    {
        $user = auth()->user();

        // Display only. Authorisation and scoping go through GuardianResolver:
        // ->first() on a non-unique column meant a guardian holding two rows
        // saw one set of children and was 403'd on the rest, and an adult
        // member with no guardian row hard-failed on every screen here.
        return app(GuardianResolver::class)->guardiansFor($user)->first()
            ?? Guardian::where('user_id', $user->id)->firstOrFail();
    }

    private function getChildrenIds(): array
    {
        return app(GuardianResolver::class)->participantIdsFor(auth()->user());
    }
}
