<?php

namespace App\Http\Controllers\Api\V1\PublicSite;

use App\Domain\Applicants\Models\Applicant;
use App\Domain\Applicants\Services\ApplicantPortalPresenter;
use App\Http\Controllers\Controller;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

/**
 * Public application tracking (§165).
 *
 * Knowing a reference number is not enough: the caller must also supply the
 * mobile number or date of birth recorded on the file, so a guessed or
 * shoulder-surfed reference alone reveals nothing.
 *
 * OTP verification is the intended long-term check; it needs an SMS provider,
 * which is not yet wired up. Until then this pairing is the guard, and the
 * response is limited to the same safe view the applicant portal shows.
 */
class PublicTrackingController extends Controller
{
    private const MAX_ATTEMPTS = 6;

    public function __construct(private readonly ApplicantPortalPresenter $presenter) {}

    public function __invoke(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'reference_no' => ['required', 'string', 'max:40'],
            'verification' => ['required', 'string', 'max:40'],
        ]);

        // Throttled by IP: tracking is unauthenticated, so it must not become a
        // way to enumerate references.
        $key = 'track:'.$request->ip();

        if (RateLimiter::tooManyAttempts($key, self::MAX_ATTEMPTS)) {
            return ApiResponse::error(
                'Too many attempts. Please try again in a few minutes.',
                [],
                429,
            );
        }

        RateLimiter::hit($key, 300);

        $applicant = Applicant::query()
            ->where('reference_no', $validated['reference_no'])
            ->whereNull('merged_into_id')
            ->with(['currentStage', 'pipeline', 'jobDemand:id,uuid,job_title'])
            ->first();

        $verification = trim($validated['verification']);

        $matches = $applicant !== null && (
            ($applicant->mobile !== null && $applicant->mobile === $verification)
            || ($applicant->date_of_birth !== null && $applicant->date_of_birth->toDateString() === $verification)
        );

        // One message for both "no such reference" and "wrong details", so the
        // endpoint cannot confirm which references exist.
        if (! $matches) {
            return ApiResponse::error(
                'We could not find an application matching those details.',
                [],
                404,
            );
        }

        RateLimiter::clear($key);

        return ApiResponse::success([
            'reference_no' => $applicant->reference_no,
            'full_name' => $applicant->full_name,
            'job_title' => $applicant->jobDemand?->job_title,
            'destination_country' => $applicant->destination_country,
            'status' => $this->presenter->currentStatus($applicant),
            'timeline' => $this->presenter->timeline($applicant),
            'next_actions' => $this->presenter->nextActions(
                collect($this->presenter->documents($applicant))
                    ->map(fn (array $document) => [
                        'document_type' => [
                            'id' => $document['document_type']['id'],
                            'name' => $document['document_type']['name'],
                        ],
                        'needs_action' => $document['needs_action'],
                        'status' => $document['status'],
                        'status_label' => $document['status_label'],
                        'document' => ['rejection_reason' => $document['rejection_reason']],
                    ])
                    ->all()
            ),
            'has_portal_account' => $applicant->user_id !== null,
        ]);
    }
}
