<?php

namespace App\Modules\HR\Recruitment\Services;

use App\Modules\HR\Recruitment\DTOs\InterviewWriteData;
use App\Modules\HR\Recruitment\Events\RecruitmentInterviewCreated;
use App\Modules\HR\Recruitment\Events\RecruitmentInterviewDeleted;
use App\Modules\HR\Recruitment\Events\RecruitmentInterviewUpdated;
use App\Modules\HR\Recruitment\Models\RecruitmentInterview;
use App\Modules\HR\Recruitment\Repositories\RecruitmentInterviewRepository;
use App\Modules\HR\Recruitment\Support\InterviewStatus;
use App\Support\SystemReferenceAssigner;
use App\Support\SystemReferenceGenerator;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

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

    public function store(InterviewWriteData $data, int $userId): RecruitmentInterview
    {
        $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::INTERVIEW),
                'status' => InterviewStatus::SCHEDULED,
                'created_by' => $userId,
            ]));

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

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

        return $row;
    }

    public function update(RecruitmentInterview $row, InterviewWriteData $data): RecruitmentInterview
    {
        $this->assertEditable($row);
        $fresh = $this->repository->update($row, $data->toArray());
        $this->events->dispatch(new RecruitmentInterviewUpdated($fresh));

        return $fresh;
    }

    public function complete(RecruitmentInterview $row): RecruitmentInterview
    {
        $this->assertStatus($row, InterviewStatus::SCHEDULED, 'only_scheduled_complete');
        $fresh = $this->repository->update($row, ['status' => InterviewStatus::COMPLETED]);
        $this->events->dispatch(new RecruitmentInterviewUpdated($fresh));

        return $fresh;
    }

    public function cancel(RecruitmentInterview $row): RecruitmentInterview
    {
        $this->assertStatus($row, InterviewStatus::SCHEDULED, 'only_scheduled_cancel');
        $fresh = $this->repository->update($row, ['status' => InterviewStatus::CANCELLED]);
        $this->events->dispatch(new RecruitmentInterviewUpdated($fresh));

        return $fresh;
    }

    public function markNoShow(RecruitmentInterview $row): RecruitmentInterview
    {
        $this->assertStatus($row, InterviewStatus::SCHEDULED, 'only_scheduled_no_show');
        $fresh = $this->repository->update($row, ['status' => InterviewStatus::NO_SHOW]);
        $this->events->dispatch(new RecruitmentInterviewUpdated($fresh));

        return $fresh;
    }

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

    /** @param  list<int>  $ids */
    public function destroyMany(array $ids): int
    {
        return $this->repository->softDeleteMany($this->tenantId(), $ids);
    }

    private function assertEditable(RecruitmentInterview $row): void
    {
        if ($row->status !== InterviewStatus::SCHEDULED) {
            throw ValidationException::withMessages([
                'status' => __('recruitment.interviews.errors.only_scheduled_editable'),
            ]);
        }
    }

    private function assertStatus(RecruitmentInterview $row, string $expected, string $errorKey): void
    {
        if ($row->status !== $expected) {
            throw ValidationException::withMessages([
                'status' => __('recruitment.interviews.errors.'.$errorKey),
            ]);
        }
    }

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

        return (int) $id;
    }
}
