<?php

namespace App\Modules\HR\Recruitment\Services;

use App\Modules\HR\Recruitment\DTOs\OfferLetterWriteData;
use App\Modules\HR\Recruitment\Events\RecruitmentOfferLetterCreated;
use App\Modules\HR\Recruitment\Events\RecruitmentOfferLetterDeleted;
use App\Modules\HR\Recruitment\Events\RecruitmentOfferLetterUpdated;
use App\Modules\HR\Recruitment\Models\RecruitmentOfferLetter;
use App\Modules\HR\Recruitment\Repositories\RecruitmentOfferLetterRepository;
use App\Modules\HR\Recruitment\Support\OfferLetterStatus;
use App\Support\SystemReferenceAssigner;
use App\Support\SystemReferenceGenerator;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class RecruitmentOfferLetterService
{
    public function __construct(
        private readonly RecruitmentOfferLetterRepository $repository,
        private readonly RecruitmentOfferLetterStatusHistoryService $history,
        private readonly SystemReferenceAssigner $references,
        private readonly Dispatcher $events,
    ) {}

    public function store(OfferLetterWriteData $data, int $userId): RecruitmentOfferLetter
    {
        $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::OFFER_LETTER),
                'status' => OfferLetterStatus::DRAFT,
                'version' => 1,
                'is_current' => true,
                'created_by' => $userId,
            ]));
            $assigned = $this->references->assign($created, SystemReferenceGenerator::OFFER_LETTER, 'reference_code');
            $this->history->record($assigned, null, OfferLetterStatus::DRAFT, 'create', $userId);

            return $assigned;
        });

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

        return $row;
    }

    public function update(RecruitmentOfferLetter $row, OfferLetterWriteData $data): RecruitmentOfferLetter
    {
        $this->assertDraft($row);
        $fresh = $this->repository->update($row, $data->toArray());
        $this->events->dispatch(new RecruitmentOfferLetterUpdated($fresh));

        return $fresh;
    }

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

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

    public function duplicate(RecruitmentOfferLetter $row, int $userId): RecruitmentOfferLetter
    {
        $payload = OfferLetterWriteData::fromValidated([
            'application_id' => $row->application_id,
            'candidate_id' => $row->candidate_id,
            'vacancy_id' => $row->vacancy_id,
            'department_id' => $row->department_id,
            'position_id' => $row->position_id,
            'employment_type' => $row->employment_type,
            'employment_status_id' => $row->employment_status_id,
            'start_date' => $row->start_date?->format('Y-m-d'),
            'probation_period_days' => $row->probation_period_days,
            'salary' => $row->salary,
            'currency' => $row->currency,
            'allowances' => $row->allowances,
            'benefits' => $row->benefits,
            'working_hours' => $row->working_hours,
            'notes' => $row->notes,
            'expires_at' => $row->expires_at?->format('Y-m-d\TH:i'),
            'acceptance_deadline_at' => $row->acceptance_deadline_at?->format('Y-m-d\TH:i'),
        ]);

        $copy = $this->store($payload, $userId);

        return $this->repository->update($copy, [
            'parent_offer_id' => $row->id,
            'version' => ((int) $row->version) + 1,
            'template_key' => $row->template_key,
            'template_version' => $row->template_version,
        ]);
    }

    private function assertDraft(RecruitmentOfferLetter $row): void
    {
        if (! in_array($row->status, OfferLetterStatus::editable(), true)) {
            throw ValidationException::withMessages([
                'status' => __('recruitment.offer_letters.errors.only_draft_editable'),
            ]);
        }
    }

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

        return (int) $id;
    }
}
