<?php

namespace App\Actions\Applications;

use App\Enums\ApplicationStatus;
use App\Models\Application;
use App\Services\Admissions\ApplicationStatusService;
use App\Services\AuditLogger;
use App\Services\Notifications\ApplicantNotifier;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

/**
 * Final gate before an application enters the admissions queue (PRD 20).
 *
 * The readiness rules live in ApplicationValidator so the wizard can show the
 * same checklist the submission enforces — the applicant never gets a surprise
 * at the last step.
 */
class SubmitApplication
{
    public function __construct(
        protected ApplicationValidator $validator,
        protected ApplicationStatusService $status,
        protected ApplicantNotifier $notifier,
        protected AuditLogger $audit,
    ) {}

    /**
     * @throws ValidationException when the application is not ready.
     */
    public function execute(Application $application): Application
    {
        if ($application->isSubmitted() && $application->status !== ApplicationStatus::NeedsInformation) {
            throw ValidationException::withMessages([
                'application' => 'This application has already been submitted.',
            ]);
        }

        $failures = $this->validator->unmetRequirements($application);

        if ($failures !== []) {
            throw ValidationException::withMessages(['application' => $failures]);
        }

        DB::transaction(function () use ($application) {
            $application->forceFill(['submitted_at' => now()])->save();
        });

        $this->status->transition(
            application: $application,
            to: ApplicationStatus::Submitted,
            actor: $application->applicant->user,
            reason: 'Submitted by applicant',
            notify: false,
        );

        $this->audit->log(AuditLogger::APPLICATION_SUBMITTED, $application, [], [
            'application_number' => $application->application_number,
        ]);

        $this->notifier->submitted($application->fresh(['applicant.user', 'choices.programme']));

        return $application->refresh();
    }
}
