<?php

namespace App\Livewire\Attendance;

use App\Domain\Attendance\Enums\AttendanceStatus;
use App\Domain\Attendance\Models\AttendanceRecord;
use App\Domain\Attendance\Services\AttendanceGenerationService;
use App\Domain\Attendance\Services\AttendanceMarkingService;
use App\Domain\Financial\Enums\InvoiceStatus;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Participant\Models\Participant;
use App\Domain\Scheduling\Models\Assignment;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Enums\SessionStatus;
use App\Domain\Training\Models\Enrollment;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingSession;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('حضور سريع')]
class QuickAttendance extends Component
{
    use UsesBranchScope;

    /**
     * Set only by selectSession(), which resolves the id through the scoped
     * model first. Locked so the browser cannot swap in another session id
     * behind every query on this screen.
     */
    #[Locked]
    public ?int $selectedSessionId = null;

    /** @var array<int, string> record_id => status */
    public array $marks = [];

    public bool $saved = false;

    public function mount(): void
    {
        $this->authorize('attendance.mark');
    }

    public function selectSession(int $sessionId, AttendanceGenerationService $generationService): void
    {
        // Resolve the id through the model rather than trusting it: BranchScope
        // makes find() return null for a session in another branch, so a forged
        // id selects nothing instead of generating attendance rows for — and
        // then rendering the roster of — a group this desk cannot see.
        $session = TrainingSession::find($sessionId);

        if (!$session) {
            $this->selectedSessionId = null;
            $this->marks = [];

            return;
        }

        $this->selectedSessionId = $session->id;
        $this->saved = false;

        $hasRecords = AttendanceRecord::where('training_session_id', $session->id)
            ->where('subject_type', Participant::class)
            ->exists();

        if (!$hasRecords) {
            $generationService->generateForSession($session);
        }

        $this->loadMarks();
    }

    public function loadMarks(): void
    {
        if (!$this->selectedSessionId) {
            $this->marks = [];
            return;
        }

        $records = AttendanceRecord::where('training_session_id', $this->selectedSessionId)
            ->where('subject_type', Participant::class)
            ->get();

        $this->marks = [];
        foreach ($records as $record) {
            // Default: if still 'expected', mark as 'present'; otherwise keep current status
            $this->marks[$record->id] = $record->status === AttendanceStatus::Expected
                ? AttendanceStatus::Present->value
                : $record->status->value;
        }
    }

    public function toggleAbsent(int $recordId): void
    {
        if (!isset($this->marks[$recordId])) {
            return;
        }

        $current = $this->marks[$recordId];
        $this->marks[$recordId] = $current === AttendanceStatus::Present->value
            ? AttendanceStatus::Absent->value
            : AttendanceStatus::Present->value;

        $this->saved = false;
    }

    public function markAllPresent(): void
    {
        foreach ($this->marks as $recordId => $status) {
            $this->marks[$recordId] = AttendanceStatus::Present->value;
        }
        $this->saved = false;
    }

    public function save(AttendanceMarkingService $service): void
    {
        if (!$this->selectedSessionId) {
            return;
        }

        $user = auth()->user();
        $marksPayload = [];

        foreach ($this->marks as $recordId => $status) {
            $marksPayload[] = [
                'record_id' => $recordId,
                'status' => $status,
            ];
        }

        $service->bulkMark($this->selectedSessionId, $marksPayload, $user);

        $this->saved = true;
        session()->flash('success', __('تم حفظ الحضور بنجاح'));
    }

    public function render()
    {
        $user = auth()->user();
        $today = now()->toDateString();

        // Get group IDs assigned to this trainer (via assignments OR head_trainer_id)
        $assignedGroupIds = Assignment::active()
            ->forUser($user->id)
            ->where('assignable_type', TrainingGroup::class)
            ->pluck('assignable_id');

        $branchId = $this->getActiveBranchId();

        $headTrainerGroupIds = TrainingGroup::where('head_trainer_id', $user->id)
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->whereIn('status', ['active', 'forming'])
            ->pluck('id');

        $allGroupIds = $assignedGroupIds->merge($headTrainerGroupIds)->unique();

        // Today's sessions for assigned groups
        $todaySessions = TrainingSession::whereIn('training_group_id', $allGroupIds)
            ->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId)))
            ->where('session_date', $today)
            ->whereIn('status', [SessionStatus::Scheduled, SessionStatus::InProgress, SessionStatus::Completed])
            ->with('group')
            ->orderBy('start_time')
            ->get();

        // Load attendance records for selected session
        $records = collect();
        $summary = ['total' => 0, 'present' => 0, 'absent' => 0];
        $notPaidParticipantIds = [];

        if ($this->selectedSessionId) {
            $records = AttendanceRecord::where('training_session_id', $this->selectedSessionId)
                ->where('subject_type', Participant::class)
                ->with('subject.person')
                ->orderBy('id')
                ->get();

            $summary['total'] = count($this->marks);
            $summary['present'] = collect($this->marks)->filter(
                fn ($s) => $s === AttendanceStatus::Present->value
            )->count();
            $summary['absent'] = $summary['total'] - $summary['present'];

            // Detect unpaid/unrenewed participants
            $participantIds = $records->pluck('subject_id')->unique()->toArray();
            if ($participantIds) {
                $session = TrainingSession::find($this->selectedSessionId);

                $unpaidInvoiceIds = Invoice::where('billable_type', Participant::class)
                    ->whereIn('billable_id', $participantIds)
                    ->whereIn('status', [InvoiceStatus::Sent, InvoiceStatus::Overdue, InvoiceStatus::PartiallyPaid])
                    ->where('due_amount', '>', 0)
                    ->whereNull('deleted_at')
                    ->pluck('billable_id')
                    ->unique()
                    ->toArray();

                $overdueEnrollmentIds = Enrollment::whereIn('participant_id', $participantIds)
                    ->where('status', 'active')
                    ->when($session, fn ($q) => $q->where('training_group_id', $session->training_group_id))
                    ->whereNotNull('next_billing_date')
                    ->where('next_billing_date', '<', now()->toDateString())
                    ->pluck('participant_id')
                    ->unique()
                    ->toArray();

                $notPaidParticipantIds = array_unique(array_merge($unpaidInvoiceIds, $overdueEnrollmentIds));
            }
        }

        return view('livewire.attendance.quick-attendance', [
            'todaySessions' => $todaySessions,
            'records' => $records,
            'summary' => $summary,
            'notPaidParticipantIds' => $notPaidParticipantIds,
        ]);
    }
}
