<?php

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

use App\Domain\Applicants\Enums\ApplicantStatus;
use App\Domain\Applicants\Models\Applicant;
use App\Domain\Applicants\Services\ApplicantService;
use App\Domain\Applicants\Services\DuplicateDetector;
use App\Domain\Organization\Models\Branch;
use App\Domain\Recruitment\Models\JobDemand;
use App\Domain\Website\Services\SiteContentService;
use App\Http\Controllers\Controller;
use App\Http\Requests\PublicSite\CandidateRegistrationRequest;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;

/**
 * Candidate self-registration from the website (§163, §164).
 *
 * The record lands in the same applicant CRM as an agent-entered one, marked
 * `source: website` and unverified — staff review every self-submission before
 * it is processed (§10).
 */
class PublicCandidateController extends Controller
{
    public function __construct(
        private readonly ApplicantService $applicants,
        private readonly DuplicateDetector $duplicates,
        private readonly SiteContentService $site,
    ) {}

    public function store(CandidateRegistrationRequest $request): JsonResponse
    {
        $company = $this->site->company();

        if ($company === null) {
            return ApiResponse::error('Registration is not available right now.', [], 503);
        }

        $attributes = $request->candidateAttributes();
        $attributes['company_id'] = $company->id;
        // Website registrations belong to the head office until staff reassign them.
        $attributes['branch_id'] = Branch::where('company_id', $company->id)
            ->orderByDesc('is_head_office')
            ->orderBy('id')
            ->value('id');

        // §163: "Do not create duplicate applicants." A returning candidate is
        // recognised and told to track their existing application instead of
        // silently getting a second file.
        $existing = $this->duplicates
            ->check($company->id, $attributes)
            ->first(fn (array $match) => $match['level'] === DuplicateDetector::HARD);

        if ($existing !== null) {
            return ApiResponse::error(
                'You have already registered with us. Use "Track your application" with your reference number.',
                ['duplicate' => ['An application already exists for this passport or national ID.']],
                409,
            );
        }

        $job = $this->resolveJob($request->input('job_slug'), $company->id);

        $applicant = DB::transaction(function () use ($attributes, $job) {
            if ($job !== null) {
                $attributes['job_demand_id'] = $job->id;
                $attributes['employer_id'] = $job->employer_id;
                $attributes['destination_country'] = $job->country;
            }

            $applicant = $this->applicants->create($attributes);

            // Self-registration is a lead until staff have checked it.
            $applicant->update(['status' => ApplicantStatus::Lead->value]);

            if ($job !== null) {
                $job->increment('shortlisted_count');
            }

            return $applicant;
        });

        return ApiResponse::success(
            [
                'reference_no' => $applicant->reference_no,
                'full_name' => $applicant->full_name,
                'applied_to' => $job?->job_title,
            ],
            'Registration received. Keep your reference number to track your application.',
            status: 201,
        );
    }

    private function resolveJob(?string $slug, int $companyId): ?JobDemand
    {
        if (blank($slug)) {
            return null;
        }

        $job = JobDemand::publiclyVisible()
            ->where('company_id', $companyId)
            ->where('public_slug', $slug)
            ->first();

        // Applying to a job that has closed still registers the candidate; it
        // simply is not attached to a demand that can no longer take them.
        return $job?->isAcceptingApplications() ? $job : null;
    }

    /** Whether a candidate is already on file, checked before the form is submitted. */
    public function checkExisting(): JsonResponse
    {
        $company = $this->site->company();

        abort_if($company === null, 503);

        $data = request()->validate([
            'passport_number' => ['nullable', 'string', 'max:50'],
            'national_id' => ['nullable', 'string', 'max:50'],
        ]);

        $exists = $this->duplicates
            ->check($company->id, $data)
            ->contains(fn (array $match) => $match['level'] === DuplicateDetector::HARD);

        return ApiResponse::success(['already_registered' => $exists]);
    }
}
