<?php

namespace App\Modules\HR\Employees\Services;

use App\Models\User;
use App\Modules\Admin\Tenancy\Models\Tenant;
use App\Modules\HR\Employees\Models\Employee;
use App\Modules\Settings\Rbac\Repositories\RoleRepository;
use App\Modules\Settings\Rbac\Services\TenantUserCreationService;
use App\Modules\Settings\Rbac\Services\TenantUserDirectoryService;
use App\Modules\Settings\Rbac\Services\TenantUserEmployeeLinkService;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Validation\ValidationException;

class EmployeeUserAccountService
{
    public function __construct(
        private readonly TenantUserCreationService $userCreation,
        private readonly TenantUserEmployeeLinkService $employeeLinks,
        private readonly TenantUserDirectoryService $directory,
        private readonly RoleRepository $roles,
    ) {}

    /**
     * @return array{
     *     linkedUser: User|null,
     *     roles: Collection,
     *     unlinkedUserOptions: array<int, string>,
     *     defaultRoleIds: list<int>
     * }
     */
    public function formContext(Tenant $tenant, Employee $employee): array
    {
        $employee->loadMissing('user.roles');

        return [
            'linkedUser' => $employee->user,
            'roles' => $this->roles->activeForTenant((int) $tenant->id),
            'unlinkedUserOptions' => $this->directory->unlinkedUserOptionsForTenant($tenant),
            'defaultRoleIds' => $this->defaultRoleIdsForEmployee((int) $tenant->id),
        ];
    }

    /**
     * @param  list<int>  $roleIds
     */
    public function createAccount(Tenant $tenant, Employee $employee, string $password, array $roleIds): User
    {
        $this->assertNoLinkedUser($employee);
        $email = trim((string) $employee->email);
        if ($email === '') {
            throw ValidationException::withMessages([
                'email' => [__('employees.user_account.validation.email_required')],
            ]);
        }

        return $this->userCreation->create(
            $tenant,
            (string) $employee->full_name,
            $email,
            $password,
            $roleIds,
            (int) $employee->id,
        );
    }

    public function linkExisting(Tenant $tenant, Employee $employee, int $userId): void
    {
        $this->assertNoLinkedUser($employee);
        $user = $this->directory->findTenantUser($tenant, $userId);
        if ($user === null) {
            throw ValidationException::withMessages([
                'user_id' => [__('employees.user_account.validation.user_invalid')],
            ]);
        }
        if ($user->employee_id !== null) {
            throw ValidationException::withMessages([
                'user_id' => [__('employees.user_account.validation.user_already_linked')],
            ]);
        }

        $this->employeeLinks->sync($tenant, $user, (int) $employee->id);
    }

    public function unlink(Tenant $tenant, Employee $employee): void
    {
        $employee->loadMissing('user');
        $user = $employee->user;
        if (! $user instanceof User) {
            return;
        }

        $this->employeeLinks->sync($tenant, $user, null);
    }

    private function assertNoLinkedUser(Employee $employee): void
    {
        $employee->loadMissing('user');
        if ($employee->user !== null) {
            throw ValidationException::withMessages([
                'user' => [__('employees.user_account.validation.already_linked')],
            ]);
        }
    }

    /**
     * @return list<int>
     */
    private function defaultRoleIdsForEmployee(int $tenantId): array
    {
        $role = $this->roles->findBySlug($tenantId, 'employee');

        return $role !== null ? [(int) $role->id] : [];
    }
}
