<?php

namespace App\Livewire\Applicant;

use App\Enums\ApplicationStep;
use App\Enums\QualificationType;
use App\Livewire\Applicant\Concerns\WizardStep;
use App\Models\Application;
use App\Models\Qualification;
use App\Support\Uganda;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Livewire\Component;

/**
 * Repeatable academic history (PRD 15).
 *
 * Which fields matter depends on the qualification type, so the form is driven
 * by QualificationType rather than a fixed shape: UCE/UACE capture subjects and
 * grades, diplomas and degrees capture a GPA and class of award.
 */
class AcademicHistoryForm extends Component
{
    use WizardStep;

    /** @var array<int, array<string, mixed>> */
    public array $qualifications = [];

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

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

        $existing = $application->applicant
            ->qualifications()
            ->with('subjects')
            ->orderBy('year')
            ->get();

        $this->qualifications = $existing->isEmpty()
            ? [$this->blankQualification(QualificationType::Uace->value)]
            : $existing->map(fn (Qualification $q) => [
                'id' => $q->id,
                'qualification_type' => $q->qualification_type->value,
                'institution' => $q->institution,
                'award' => $q->award ?? '',
                'year' => (string) $q->year,
                'index_number' => $q->index_number ?? '',
                'gpa' => $q->gpa !== null ? (string) $q->gpa : '',
                'subjects' => $q->subjects->map(fn ($s) => [
                    'subject' => $s->subject,
                    'grade' => $s->grade,
                ])->values()->all() ?: [['subject' => '', 'grade' => '']],
            ])->values()->all();
    }

    protected function blankQualification(string $type = 'uace'): array
    {
        return [
            'id' => null,
            'qualification_type' => $type,
            'institution' => '',
            'award' => '',
            'year' => '',
            'index_number' => '',
            'gpa' => '',
            'subjects' => [['subject' => '', 'grade' => '']],
        ];
    }

    public function addQualification(): void
    {
        $this->qualifications[] = $this->blankQualification();
    }

    public function removeQualification(int $index): void
    {
        unset($this->qualifications[$index]);
        $this->qualifications = array_values($this->qualifications);

        if ($this->qualifications === []) {
            $this->qualifications = [$this->blankQualification()];
        }
    }

    public function addSubject(int $index): void
    {
        $this->qualifications[$index]['subjects'][] = ['subject' => '', 'grade' => ''];
    }

    public function removeSubject(int $index, int $subjectIndex): void
    {
        unset($this->qualifications[$index]['subjects'][$subjectIndex]);
        $this->qualifications[$index]['subjects'] = array_values($this->qualifications[$index]['subjects']);

        if ($this->qualifications[$index]['subjects'] === []) {
            $this->qualifications[$index]['subjects'] = [['subject' => '', 'grade' => '']];
        }
    }

    protected function rules(): array
    {
        return [
            'qualifications' => ['required', 'array', 'min:1'],
            'qualifications.*.id' => ['nullable', 'integer'],
            'qualifications.*.qualification_type' => ['required', Rule::enum(QualificationType::class)],
            'qualifications.*.institution' => ['required', 'string', 'max:190'],
            'qualifications.*.award' => ['nullable', 'string', 'max:190'],
            'qualifications.*.year' => ['required', 'integer', 'min:1960', 'max:'.(now()->year + 1)],
            'qualifications.*.index_number' => ['nullable', 'string', 'max:60'],
            'qualifications.*.gpa' => ['nullable', 'numeric', 'min:0', 'max:5'],
            'qualifications.*.subjects' => ['array'],
            'qualifications.*.subjects.*.subject' => ['nullable', 'string', 'max:120'],
            'qualifications.*.subjects.*.grade' => ['nullable', 'string', 'max:10'],
        ];
    }

    protected function validationAttributes(): array
    {
        return [
            'qualifications.*.institution' => 'institution',
            'qualifications.*.year' => 'year',
            'qualifications.*.qualification_type' => 'qualification type',
        ];
    }

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

        // Subject-bearing qualifications need at least one complete subject row.
        foreach ($data['qualifications'] as $index => $qualification) {
            $type = QualificationType::from($qualification['qualification_type']);

            if (! $type->hasSubjects()) {
                continue;
            }

            $complete = collect($qualification['subjects'] ?? [])
                ->filter(fn (array $s) => filled($s['subject']) && filled($s['grade']));

            if ($complete->isEmpty()) {
                $this->addError(
                    "qualifications.{$index}.subjects",
                    'Add at least one subject and grade for this qualification.',
                );
            }
        }

        if ($this->getErrorBag()->isNotEmpty()) {
            return;
        }

        $applicant = $this->application->applicant;

        DB::transaction(function () use ($data, $applicant) {
            $keptIds = [];

            foreach ($data['qualifications'] as $row) {
                $type = QualificationType::from($row['qualification_type']);

                $attributes = [
                    'applicant_id' => $applicant->id,
                    'qualification_type' => $type,
                    'institution' => $row['institution'],
                    'award' => ($row['award'] ?? '') ?: null,
                    'year' => (int) $row['year'],
                    'index_number' => ($row['index_number'] ?? '') ?: null,
                    'gpa' => $type->hasGpa() && ($row['gpa'] ?? '') !== '' ? (float) $row['gpa'] : null,
                ];

                // Only an existing row belonging to THIS applicant may be updated;
                // an unrecognised id creates a new record rather than touching
                // another applicant's qualification.
                $qualification = filled($row['id'] ?? null)
                    ? $applicant->qualifications()->whereKey($row['id'])->first()
                    : null;

                $qualification
                    ? $qualification->update($attributes)
                    : $qualification = Qualification::create($attributes);

                $keptIds[] = $qualification->id;

                $qualification->subjects()->delete();

                if (! $type->hasSubjects()) {
                    continue;
                }

                $subjects = collect($row['subjects'] ?? [])
                    ->filter(fn (array $s) => filled($s['subject']) && filled($s['grade']))
                    ->map(fn (array $s) => [
                        'subject' => trim($s['subject']),
                        'grade' => strtoupper(trim($s['grade'])),
                    ]);

                $qualification->subjects()->createMany($subjects->all());

                // Cache the weighted total so eligibility checks stay cheap.
                if ($type === QualificationType::Uace) {
                    $qualification->update([
                        'total_points' => app(\App\Services\Admissions\EligibilityService::class)
                            ->uacePoints($qualification->load('subjects')),
                    ]);
                }
            }

            // Remove qualifications the applicant deleted in this session.
            $applicant->qualifications()->whereNotIn('id', $keptIds)->delete();
        });

        $this->completeStep();
    }

    public function render()
    {
        return view('livewire.applicant.academic-history-form', [
            'types' => QualificationType::options(),
            'uaceGrades' => Uganda::uaceGrades(),
            'uceGrades' => Uganda::uceGrades(),
            'subjectSuggestions' => Uganda::subjects(),
        ]);
    }
}
