<?php

namespace App\Modules\HR\Recruitment\Services;

use App\Modules\HR\Recruitment\DTOs\VacancyWriteData;
use App\Modules\HR\Recruitment\Events\RecruitmentVacancyCreated;
use App\Modules\HR\Recruitment\Events\RecruitmentVacancyDeleted;
use App\Modules\HR\Recruitment\Events\RecruitmentVacancyUpdated;
use App\Modules\HR\Recruitment\Models\RecruitmentJobRequest;
use App\Modules\HR\Recruitment\Models\RecruitmentVacancy;
use App\Modules\HR\Recruitment\Repositories\RecruitmentVacancyRepository;
use App\Modules\HR\Recruitment\Support\JobRequestStatus;
use App\Modules\HR\Recruitment\Support\VacancyStatus;
use App\Support\SystemReferenceAssigner;
use App\Support\SystemReferenceGenerator;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class RecruitmentVacancyService
{
    public function __construct(
        private readonly RecruitmentVacancyRepository $repository,
        private readonly SystemReferenceAssigner $references,
        private readonly Dispatcher $events,
    ) {}

    public function store(VacancyWriteData $data, int $userId): RecruitmentVacancy
    {
        $this->assertApprovedJobRequest($data->jobRequestId);

        $row = DB::transaction(function () use ($data, $userId) {
            $created = $this->repository->create(array_merge($data->toArray(), [
                'tenant_id' => $this->tenantId(),
                'reference_code' => $this->references->pendingPlaceholder(SystemReferenceGenerator::VACANCY),
                'status' => VacancyStatus::DRAFT,
                'created_by' => $userId,
            ]));

            return $this->references->assign($created, SystemReferenceGenerator::VACANCY, 'reference_code');
        });

        $this->events->dispatch(new RecruitmentVacancyCreated($row));

        return $row;
    }

    public function update(RecruitmentVacancy $row, VacancyWriteData $data): RecruitmentVacancy
    {
        $this->assertApprovedJobRequest($data->jobRequestId);
        $fresh = $this->repository->update($row, $data->toArray());
        $this->events->dispatch(new RecruitmentVacancyUpdated($fresh));

        return $fresh;
    }

    public function destroy(RecruitmentVacancy $row): void
    {
        $this->repository->softDelete($row);
        $this->events->dispatch(new RecruitmentVacancyDeleted($row));
    }

    public function publish(RecruitmentVacancy $row): RecruitmentVacancy
    {
        if ($row->status !== VacancyStatus::DRAFT) {
            throw ValidationException::withMessages([
                'status' => __('recruitment.vacancies.errors.only_draft_publish'),
            ]);
        }
        $fresh = $this->repository->update($row, [
            'status' => VacancyStatus::OPEN,
            'published_at' => now(),
        ]);
        $this->events->dispatch(new RecruitmentVacancyUpdated($fresh));

        return $fresh;
    }

    public function close(RecruitmentVacancy $row): RecruitmentVacancy
    {
        if (! in_array($row->status, [VacancyStatus::OPEN, VacancyStatus::ON_HOLD], true)) {
            throw ValidationException::withMessages([
                'status' => __('recruitment.vacancies.errors.only_open_close'),
            ]);
        }
        $fresh = $this->repository->update($row, ['status' => VacancyStatus::CLOSED]);
        $this->events->dispatch(new RecruitmentVacancyUpdated($fresh));

        return $fresh;
    }

    private function assertApprovedJobRequest(int $jobRequestId): void
    {
        $jr = RecruitmentJobRequest::query()->whereKey($jobRequestId)->first();
        if ($jr === null || $jr->status !== JobRequestStatus::APPROVED) {
            throw ValidationException::withMessages([
                'job_request_id' => __('recruitment.vacancies.errors.job_request_must_be_approved'),
            ]);
        }
    }

    private function tenantId(): int
    {
        $id = auth()->user()?->tenant_id;
        abort_if($id === null, 403);

        return (int) $id;
    }
}
