<?php

namespace App\Livewire\Applicant;

use App\Enums\ApplicationStep;
use App\Livewire\Applicant\Concerns\WizardStep;
use App\Models\Application;
use App\Models\Programme;
use App\Services\Admissions\EligibilityService;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Livewire\Component;

/**
 * Programme choices for the selected intake (PRD 16).
 *
 * Only programmes actually open for this intake are offered, and each choice
 * is checked against its entry requirements. The check is advisory: it warns
 * the applicant but never blocks the choice.
 */
class ProgrammeSelectionForm extends Component
{
    use WizardStep;

    public string $campus = '';
    public string $study_mode = '';

    /** @var array<int, string> choice number => programme id */
    public array $choices = [];

    protected function step(): ApplicationStep
    {
        return ApplicationStep::Programme;
    }

    public function mount(Application $application): void
    {
        $this->mountWizardStep($application);

        $this->campus = $application->campus ?? '';
        $this->study_mode = $application->study_mode ?? '';

        $max = (int) setting('max_programme_choices', config('kcu.application.max_programme_choices'));

        for ($i = 1; $i <= $max; $i++) {
            $this->choices[$i] = (string) ($application->choices->firstWhere('choice_number', $i)?->programme_id ?? '');
        }
    }

    protected function rules(): array
    {
        return [
            'campus' => ['nullable', 'string', 'max:100'],
            'study_mode' => ['nullable', 'string', 'max:30'],
            'choices' => ['required', 'array'],
            'choices.1' => ['required', 'integer', 'exists:programmes,id'],
            'choices.*' => ['nullable', 'integer', 'exists:programmes,id'],
        ];
    }

    protected function messages(): array
    {
        return [
            'choices.1.required' => 'Please select your first choice programme.',
        ];
    }

    /** Programmes open for this intake, grouped by school for the picker. */
    public function getAvailableProgrammesProperty(): Collection
    {
        return Programme::active()
            ->openForIntake($this->application->intake)
            ->with('school:id,name')
            ->ordered()
            ->get()
            ->groupBy(fn (Programme $p) => $p->school->name);
    }

    /** Advisory eligibility for each selected choice. */
    public function getEligibilityProperty(): Collection
    {
        $service = app(EligibilityService::class);
        $applicant = $this->application->applicant->load('qualifications.subjects');

        return collect($this->choices)
            ->filter()
            ->mapWithKeys(function (string $programmeId, int $choiceNumber) use ($service, $applicant) {
                $programme = Programme::with('requirements')->find($programmeId);

                return [$choiceNumber => $programme
                    ? $service->evaluate($applicant, $programme)
                    : null];
            })
            ->filter();
    }

    public function save(): void
    {
        $data = $this->validate();

        $selected = collect($data['choices'])->filter();

        if ($selected->duplicates()->isNotEmpty()) {
            $this->addError('choices', 'Please choose a different programme for each preference.');

            return;
        }

        $service = app(EligibilityService::class);
        $applicant = $this->application->applicant->load('qualifications.subjects');

        DB::transaction(function () use ($selected, $service, $applicant) {
            $this->application->choices()->delete();

            foreach ($selected as $choiceNumber => $programmeId) {
                $programme = Programme::with('requirements')->find($programmeId);
                $result = $service->evaluate($applicant, $programme);

                $this->application->choices()->create([
                    'programme_id' => $programmeId,
                    'choice_number' => $choiceNumber,
                    'is_eligible' => $result->isEligible(),
                    'eligibility_report' => $result->toArray(),
                ]);
            }

            $this->application->fill([
                'campus' => $this->campus ?: null,
                'study_mode' => $this->study_mode ?: null,
                'applicant_type' => Programme::find($selected->first())?->level,
            ]);
        });

        $this->completeStep();
    }

    public function render()
    {
        return view('livewire.applicant.programme-selection-form', [
            'grouped' => $this->availableProgrammes,
            'eligibility' => $this->eligibility,
            'campuses' => Programme::active()->pluck('campuses')->flatten()->filter()->unique()->sort()->values(),
            'modes' => \App\Enums\StudyMode::options(),
        ]);
    }
}
