<?php

namespace App\Modules\HR\Positions\Services;

use App\Modules\HR\Departments\Models\Department;
use App\Modules\HR\Organization\Repositories\JobGradeRepository;
use App\Modules\HR\Positions\DTOs\PositionWriteData;
use App\Modules\HR\Positions\Events\PositionCreated;
use App\Modules\HR\Positions\Events\PositionDeactivated;
use App\Modules\HR\Positions\Events\PositionUpdated;
use App\Modules\HR\Positions\Models\Position;
use App\Modules\HR\Positions\Repositories\PositionRepository;
use App\Support\SystemReferenceAssigner;
use App\Support\SystemReferenceGenerator;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Validation\ValidationException;

class PositionService
{
    public function __construct(
        private readonly PositionRepository $repository,
        private readonly Dispatcher $events,
        private readonly SystemReferenceAssigner $references,
        private readonly JobGradeRepository $jobGrades,
    ) {}

    public function paginateForIndex(int $perPage = 20): LengthAwarePaginator
    {
        return $this->repository->paginateForList($perPage);
    }

    public function store(PositionWriteData $data): Position
    {
        $department = Department::query()->findOrFail($data->departmentId);
        $tenantId = (int) $department->tenant_id;
        if ($tenantId === 0) {
            throw ValidationException::withMessages([
                'department_id' => [__('positions.validation.department_tenant')],
            ]);
        }
        $this->ensureJobGradeTenant($data->jobGradeId, $tenantId);

        $payload = array_merge($data->toArray(), [
            'tenant_id' => $tenantId,
            'created_by' => auth()->id(),
            'salary' => number_format((float) $data->salary, 2, '.', ''),
        ]);

        $position = $this->repository->create($payload);
        $this->references->assign($position, SystemReferenceGenerator::POSITION, 'code');
        $position = $this->repository->findOrFail($position->id);
        $this->events->dispatch(new PositionCreated($position));

        return $position;
    }

    public function update(Position $position, PositionWriteData $data): Position
    {
        $department = Department::query()->findOrFail($data->departmentId);
        if ((int) $department->tenant_id !== (int) $position->tenant_id) {
            throw ValidationException::withMessages([
                'department_id' => [__('positions.validation.department_position_tenant')],
            ]);
        }
        $this->ensureJobGradeTenant($data->jobGradeId, (int) $position->tenant_id);

        $payload = array_merge($data->toArray(), [
            'updated_by' => auth()->id(),
            'salary' => number_format((float) $data->salary, 2, '.', ''),
        ]);

        $updated = $this->repository->update($position, $payload);
        $this->events->dispatch(new PositionUpdated($updated));

        return $updated;
    }

    public function deactivate(Position $position): void
    {
        if (! $position->is_active) {
            return;
        }

        $this->repository->update($position, [
            'is_active' => false,
            'updated_by' => auth()->id(),
        ]);
        $fresh = $this->repository->findOrFail($position->id);
        $this->events->dispatch(new PositionDeactivated($fresh));
    }

    private function ensureJobGradeTenant(?int $jobGradeId, int $tenantId): void
    {
        if ($jobGradeId === null) {
            return;
        }
        $grade = $this->jobGrades->find($jobGradeId);
        if ($grade === null || (int) $grade->tenant_id !== $tenantId) {
            throw ValidationException::withMessages([
                'job_grade_id' => [__('positions.validation.job_grade_tenant')],
            ]);
        }
    }
}
