<?php

namespace App\Livewire\Enrollments;

use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\Enrollment;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Services\EnrollmentService;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;

/**
 * Move one player from one training group to another.
 *
 * Deliberately NOT cross-branch. Every query below runs under the branch global
 * scope, so both the enrolment being moved and the destination group come from
 * the branch the user is standing in. Moving a player to another branch needs
 * more than a wider group list: EnrollmentService::transfer() creates the
 * replacement enrolment with no explicit branch, so it would be stamped with the
 * *session's* branch while pointing at the other branch's group — a row visible
 * from the wrong branch and invisible from the right one. Until the service
 * takes an explicit branch, widening this screen would corrupt data, not enable
 * a feature.
 */
#[Layout('layouts.app')]
#[Title('نقل إلى مجموعة')]
class TransferGroup extends Component
{
    use UsesBranchScope;

    public string $search = '';

    // Set only by selectParticipant()/selectEnrollment() below. Locked because a
    // browser-settable id feeding a lookup is an authorisation question, not a
    // filter — and #[Locked] is what makes that visible.
    #[Locked]
    public ?int $participant_id = null;

    #[Locked]
    public ?int $enrollment_id = null;

    // Genuinely user-chosen: it is bound to the <select>. Its guard is the
    // branch-scoped exists rule below plus the scoped findOrFail in transfer().
    public ?int $destination_group_id = null;

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

    public function rules(): array
    {
        // `exists:` is a raw table query and never sees the branch global scope.
        // Null branch means all-branches mode, where there is nothing to narrow.
        $branchId = $this->getActiveBranchId();
        $inBranch = fn (string $table) => $branchId === null
            ? Rule::exists($table, 'id')
            : Rule::exists($table, 'id')->where('branch_id', $branchId);

        return [
            'participant_id' => ['required', $inBranch('participants')],
            'enrollment_id' => ['required', $inBranch('enrollments')],
            'destination_group_id' => ['required', $inBranch('training_groups')],
        ];
    }

    public function messages(): array
    {
        return [
            'participant_id.required' => 'اختيار المشترك مطلوب',
            'participant_id.exists' => 'المشترك المختار غير موجود',
            'enrollment_id.required' => 'اختيار التسجيل الحالي مطلوب',
            'enrollment_id.exists' => 'التسجيل المختار غير موجود',
            'destination_group_id.required' => 'اختيار المجموعة الجديدة مطلوب',
            'destination_group_id.exists' => 'المجموعة المختارة غير موجودة',
        ];
    }

    public function updatedSearch(): void
    {
        $this->participant_id = null;
        $this->enrollment_id = null;
        $this->destination_group_id = null;
    }

    public function clearParticipant(): void
    {
        $this->reset(['participant_id', 'enrollment_id', 'destination_group_id']);
    }

    public function selectParticipant(int $id): void
    {
        // Resolve through the model so the branch scope decides, rather than
        // storing the raw id and finding out later that the list came back empty.
        $this->participant_id = Participant::findOrFail($id)->id;
        $this->enrollment_id = null;
        $this->destination_group_id = null;
    }

    public function selectEnrollment(int $id): void
    {
        $this->enrollment_id = Enrollment::findOrFail($id)->id;
        $this->destination_group_id = null;
    }

    public function transfer(EnrollmentService $service): void
    {
        $this->validate();

        try {
            // Both lookups run under the branch global scope: an id from another
            // branch resolves to nothing and 404s instead of transferring.
            $enrollment = Enrollment::findOrFail($this->enrollment_id);
            $toGroup = TrainingGroup::findOrFail($this->destination_group_id);

            $service->transfer($enrollment, $toGroup, auth()->user());

            session()->flash('success', __('تم نقل المشترك بنجاح إلى المجموعة الجديدة'));
            $this->redirect(route('enrollments.list'), navigate: true);
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function render()
    {
        // Participant, Enrollment and TrainingGroup all carry the branch global
        // scope, so none of the queries below repeats the branch clause.

        // Search participants
        $participants = collect();
        if (strlen($this->search) >= 2) {
            $participants = Participant::with('person')
                ->whereIn('status', ['registered', 'active'])
                ->where(function ($q) {
                    $q->whereHas('person', function ($pq) {
                        $pq->where('name_ar', 'ilike', "%{$this->search}%")
                           ->orWhere('name', 'ilike', "%{$this->search}%");
                    })->orWhere('participant_number', 'ilike', "%{$this->search}%");
                })
                ->limit(20)
                ->get();
        }

        // Active enrollments for selected participant
        $activeEnrollments = collect();
        if ($this->participant_id) {
            $activeEnrollments = Enrollment::with(['group', 'program'])
                ->where('participant_id', $this->participant_id)
                ->where('status', 'active')
                ->get();
        }

        // Available destination groups (excluding current group)
        $destinationGroups = collect();
        if ($this->enrollment_id) {
            $enrollment = Enrollment::find($this->enrollment_id);
            if ($enrollment) {
                $destinationGroups = TrainingGroup::with('program')
                    ->whereIn('status', ['forming', 'active'])
                    ->where('id', '!=', $enrollment->training_group_id)
                    ->whereColumn('current_count', '<', 'max_capacity')
                    ->orderBy('name_ar')
                    ->get();
            }
        }

        return view('livewire.enrollments.transfer-group', [
            'participants' => $participants,
            'activeEnrollments' => $activeEnrollments,
            'destinationGroups' => $destinationGroups,
        ]);
    }
}
