<?php

namespace App\Modules\HR\Employees\Requests;

use App\Modules\HR\Departments\Models\Department;
use App\Modules\HR\Employees\Models\Employee;
use App\Modules\HR\Positions\Models\Position;
use Illuminate\Http\Request;
use Illuminate\Validation\Validator;

final class EmployeeRequestValidator
{
    private const HIERARCHY_MAX_DEPTH = 50;

    public static function after(Validator $validator, Request $request, ?int $ignoreEmployeeId): void
    {
        if ($validator->errors()->isNotEmpty()) {
            return;
        }
        $deptId = (int) $request->input('department_id');
        $dept = Department::query()->find($deptId);
        if ($dept === null) {
            return;
        }
        $tenantId = (int) $dept->tenant_id;
        $nationalId = (string) $request->input('national_id');

        if (self::nationalIdTaken($tenantId, $nationalId, $ignoreEmployeeId)) {
            $validator->errors()->add('national_id', __('employees.validation.national_id_unique'));
        }

        $pos = Position::query()->find((int) $request->input('position_id'));
        if ($pos === null) {
            return;
        }
        if ((int) $pos->tenant_id !== $tenantId) {
            $validator->errors()->add('position_id', __('employees.validation.position_tenant'));
        }
        if ((int) $pos->department_id !== $deptId) {
            $validator->errors()->add('position_id', __('employees.validation.position_department'));
        }

        $user = $request->user();
        if ($user !== null && ! $user->isAdmin() && (int) $dept->tenant_id !== (int) $user->tenant_id) {
            $validator->errors()->add('department_id', __('employees.validation.department_tenant'));
        }

        $managerId = $request->input('manager_employee_id');
        if ($managerId !== null && $managerId !== '') {
            $managerEmployeeId = (int) $managerId;
            self::validateManagerHierarchy($validator, $tenantId, $ignoreEmployeeId, $managerEmployeeId);
        }
    }

    private static function validateManagerHierarchy(Validator $validator, int $tenantId, ?int $employeeId, int $managerEmployeeId): void
    {
        if ($employeeId !== null && $managerEmployeeId === $employeeId) {
            $validator->errors()->add('manager_employee_id', __('employees.validation.manager_self'));

            return;
        }

        $manager = Employee::query()
            ->where('tenant_id', $tenantId)
            ->whereKey($managerEmployeeId)
            ->first();
        if ($manager === null) {
            $validator->errors()->add('manager_employee_id', __('employees.validation.manager_tenant'));

            return;
        }

        if ($employeeId === null) {
            return;
        }

        if (self::createsCircularHierarchy($tenantId, $employeeId, $managerEmployeeId)) {
            $validator->errors()->add('manager_employee_id', __('employees.validation.manager_circular'));
        }
    }

    private static function createsCircularHierarchy(int $tenantId, int $employeeId, int $managerEmployeeId): bool
    {
        $seen = [];
        $cur = $managerEmployeeId;
        $depth = 0;

        while ($cur > 0 && $depth++ < self::HIERARCHY_MAX_DEPTH) {
            if ($cur === $employeeId) {
                return true;
            }
            if (isset($seen[$cur])) {
                return true;
            }
            $seen[$cur] = true;
            $next = Employee::query()
                ->where('tenant_id', $tenantId)
                ->whereKey($cur)
                ->value('manager_employee_id');
            $cur = $next !== null ? (int) $next : 0;
        }

        return false;
    }

    private static function nationalIdTaken(int $tenantId, string $nationalId, ?int $ignoreId): bool
    {
        $q = Employee::query()
            ->where('tenant_id', $tenantId)
            ->where('national_id', $nationalId);
        if ($ignoreId !== null) {
            $q->where('id', '!=', $ignoreId);
        }

        return $q->exists();
    }
}
