<?php

namespace MintyPHP\Service\User;

use MintyPHP\I18n;
use MintyPHP\Repository\Support\DatabaseSessionRepository;
use MintyPHP\Repository\User\UserListQueryRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\Tenant\TenantScopeService;

class UserAccountService
{
    public function __construct(
        private readonly UserReadRepositoryInterface $userReadRepository,
        private readonly UserWriteRepositoryInterface $userWriteRepository,
        private readonly UserListQueryRepositoryInterface $userListQueryRepository,
        private readonly UserAssignmentService $userAssignmentService,
        private readonly UserPasswordService $userPasswordService,
        private readonly UserSettingsGateway $settingsGateway,
        private readonly TenantScopeService $scopeGateway,
        private readonly UserDirectoryGateway $directoryGateway,
        private readonly SystemAuditService $systemAuditService,
        private readonly DatabaseSessionRepository $databaseSessionRepository
    ) {
    }

    public function listPaged(array $options): array
    {
        // Users with global tenant scope see all tenants — remove the filter so the query isn't restricted.
        if (!empty($options['tenantUserId'])) {
            $tenantUserId = (int) $options['tenantUserId'];
            if ($tenantUserId > 0 && $this->scopeGateway->hasGlobalAccess($tenantUserId)) {
                unset($options['tenantUserId']);
            }
        }
        return $this->userListQueryRepository->listPaged($options);
    }

    public function findByUuid(string $uuid): ?array
    {
        return $this->userReadRepository->findByUuid($uuid);
    }

    public function findById(int $id): ?array
    {
        return $this->userReadRepository->find($id);
    }

    public function findByEmail(string $email): ?array
    {
        return $this->userReadRepository->findByEmail($email);
    }

    public function setLocale(int $userId, string $locale): bool
    {
        return $this->userWriteRepository->setLocale($userId, $locale);
    }

    public function setTheme(int $userId, string $theme): bool
    {
        $theme = $this->normalizeTheme($theme);
        return $this->userWriteRepository->setTheme($userId, $theme);
    }

    public function deleteByUuid(string $uuid, int $currentUserId = 0): array
    {
        $uuid = trim($uuid);
        if ($uuid === '') {
            return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
        }

        $user = $this->userReadRepository->findByUuid($uuid);
        if (!$user || !isset($user['id'])) {
            return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
        }

        $userId = (int) $user['id'];
        if ($currentUserId && $currentUserId === $userId) {
            return [
                'ok' => false,
                'status' => 400,
                'error' => 'self_delete',
                'message' => t('You cannot delete your own account'),
            ];
        }

        $deleted = $this->userWriteRepository->delete($userId);
        if (!$deleted) {
            return ['ok' => false, 'status' => 500, 'error' => 'delete_failed'];
        }

        $this->systemAuditService->record('admin.users.delete', 'success', [
            'actor_user_id' => $currentUserId > 0 ? $currentUserId : null,
            'target_type' => 'user',
            'target_id' => $userId,
            'target_uuid' => (string) ($user['uuid'] ?? ''),
        ]);

        return ['ok' => true, 'user' => $user];
    }

    public function deleteByUuids(array $uuids, int $currentUserId = 0): array
    {
        $uuids = array_values(array_filter(array_map('trim', $uuids)));
        if (!$uuids) {
            return ['ok' => false, 'error' => 'no_selection'];
        }

        if ($currentUserId > 0) {
            $uuids = $this->filterUuidsByTenantScope($uuids, $currentUserId);
            if (!$uuids) {
                return ['ok' => false, 'error' => 'permission_denied'];
            }
        }

        if ($currentUserId > 0) {
            $currentUser = $this->userReadRepository->find($currentUserId);
            $currentUuid = $currentUser['uuid'] ?? '';
            if ($currentUuid !== '') {
                $uuids = array_values(array_filter($uuids, static fn ($uuid) => $uuid !== $currentUuid));
            }
            if (!$uuids) {
                return ['ok' => false, 'error' => 'self_delete'];
            }
        }

        $deleted = $this->userWriteRepository->deleteByUuids($uuids);
        if (!$deleted) {
            return ['ok' => false, 'error' => 'delete_failed'];
        }

        $this->systemAuditService->record('admin.users.bulk_update', 'success', [
            'actor_user_id' => $currentUserId > 0 ? $currentUserId : null,
            'target_type' => 'user',
            'metadata' => [
                'action' => 'delete',
                'count' => count($uuids),
                'target_uuids' => $uuids,
            ],
        ]);

        return ['ok' => true, 'count' => count($uuids)];
    }

    public function createFromAdmin(array $input, int $currentUserId = 0): array
    {
        $form = $this->sanitizeBase($input);
        $form['totp_secret'] = trim((string) ($input['totp_secret'] ?? ''));
        $form['theme'] = $this->normalizeTheme($input['theme'] ?? null);
        $form['locale'] = $this->normalizeLocale($input['locale'] ?? null);
        $primaryTenantId = (int) ($input['primary_tenant_id'] ?? 0);
        if (array_key_exists('active', $input)) {
            $form['active'] = isset($input['active']) ? 1 : 0;
        } else {
            $form['active'] = 1;
        }
        $password = (string) ($input['password'] ?? '');
        $password2 = (string) ($input['password2'] ?? '');

        $errors = $this->validateBase($form);
        $errors = array_merge($errors, $this->userPasswordService->validatePassword($password, $password2, true, $form['email']));

        $tenantIds = $input['tenant_ids'] ?? [];
        if (!is_array($tenantIds)) {
            $tenantIds = [$tenantIds];
        }
        $tenantIds = $this->normalizeTenantIds($tenantIds);
        if (!$tenantIds) {
            $defaultTenantId = $this->settingsGateway->getDefaultTenantId();
            if ($defaultTenantId) {
                $tenantIds = [$defaultTenantId];
            }
        }
        [$primaryTenantId, $primaryErrors] = $this->normalizePrimaryTenant($primaryTenantId, $tenantIds);
        $errors = array_merge($errors, $primaryErrors);

        if ($errors) {
            $form['primary_tenant_id'] = $primaryTenantId;
            return ['ok' => false, 'errors' => $errors, 'form' => $form];
        }

        $activeChangedAt = gmdate('Y-m-d H:i:s');

        $transactionStarted = false;
        try {
            $this->databaseSessionRepository->beginTransaction();
            $transactionStarted = true;

            $created = $this->userWriteRepository->create([
                'first_name' => $form['first_name'],
                'last_name' => $form['last_name'],
                'email' => $form['email'],
                'profile_description' => $form['profile_description'] !== '' ? $form['profile_description'] : null,
                'job_title' => $form['job_title'] !== '' ? $form['job_title'] : null,
                'phone' => $form['phone'] !== '' ? $form['phone'] : null,
                'mobile' => $form['mobile'] !== '' ? $form['mobile'] : null,
                'short_dial' => $form['short_dial'] !== '' ? $form['short_dial'] : null,
                'address' => $form['address'] !== '' ? $form['address'] : null,
                'postal_code' => $form['postal_code'] !== '' ? $form['postal_code'] : null,
                'city' => $form['city'] !== '' ? $form['city'] : null,
                'country' => $form['country'] !== '' ? $form['country'] : null,
                'region' => $form['region'] !== '' ? $form['region'] : null,
                'hire_date' => $form['hire_date'] !== '' ? $form['hire_date'] : null,
                'password' => $password,
                'locale' => $form['locale'],
                'totp_secret' => $form['totp_secret'],
                'theme' => $form['theme'],
                'primary_tenant_id' => $primaryTenantId > 0 ? $primaryTenantId : null,
                'active' => $form['active'],
                'created_by' => $currentUserId > 0 ? $currentUserId : null,
                'active_changed_at' => $activeChangedAt,
                'active_changed_by' => $currentUserId > 0 ? $currentUserId : null,
            ]);

            if (!$created) {
                $this->rollbackQuietly();
                return ['ok' => false, 'errors' => [t('User can not be registered')], 'form' => $form];
            }

            $createdUser = $this->userReadRepository->findByEmail($form['email']);
            $uuid = $createdUser['uuid'] ?? null;
            $userId = (int) ($createdUser['id'] ?? 0);
            if ($userId <= 0) {
                $this->rollbackQuietly();
                return ['ok' => false, 'errors' => [t('User can not be registered')], 'form' => $form];
            }

            if ($tenantIds && !$this->userAssignmentService->syncTenants($userId, $tenantIds)) {
                $this->rollbackQuietly();
                return ['ok' => false, 'errors' => [t('User can not be registered')], 'form' => $form];
            }

            $roleIds = $this->userAssignmentService->normalizeIdInput($input['role_ids'] ?? []);
            if (!$roleIds) {
                $defaultRoleId = $this->settingsGateway->getDefaultRoleId();
                if ($defaultRoleId) {
                    $roleIds = [$defaultRoleId];
                }
            }
            if ($roleIds && !$this->userAssignmentService->syncRoles($userId, $roleIds)) {
                $this->rollbackQuietly();
                return ['ok' => false, 'errors' => [t('User can not be registered')], 'form' => $form];
            }

            $departmentIds = $this->userAssignmentService->normalizeIdInput($input['department_ids'] ?? []);
            if (!$departmentIds) {
                $defaultDepartmentId = $this->settingsGateway->getDefaultDepartmentId();
                if ($defaultDepartmentId) {
                    $departmentIds = [$defaultDepartmentId];
                }
            }
            if ($departmentIds && !$this->userAssignmentService->syncDepartments($userId, $departmentIds)) {
                $this->rollbackQuietly();
                return ['ok' => false, 'errors' => [t('User can not be registered')], 'form' => $form];
            }

            $this->databaseSessionRepository->commitTransaction();
            $transactionStarted = false;
        } catch (\Throwable $exception) {
            if ($transactionStarted) {
                $this->rollbackQuietly();
            }
            return ['ok' => false, 'errors' => [t('User can not be registered')], 'form' => $form];
        }

        $this->systemAuditService->record('admin.users.create', 'success', [
            'actor_user_id' => $currentUserId > 0 ? $currentUserId : null,
            'target_type' => 'user',
            'target_id' => $userId,
            'target_uuid' => is_string($uuid) ? $uuid : '',
            'metadata' => [
                'assigned_tenant_ids' => $tenantIds,
                'assigned_role_ids' => $roleIds,
                'assigned_department_ids' => $departmentIds,
            ],
        ]);

        return ['ok' => true, 'form' => $form, 'uuid' => $uuid];
    }

    public function updateFromAdmin(int $userId, array $input, int $currentUserId = 0): array
    {
        $form = $this->sanitizeBase($input);
        $form['totp_secret'] = trim((string) ($input['totp_secret'] ?? ''));
        $form['locale'] = $this->normalizeLocale($input['locale'] ?? null);
        $themeProvided = array_key_exists('theme', $input);
        $primaryTenantId = (int) ($input['primary_tenant_id'] ?? 0);
        $tenantIdsProvided = array_key_exists('tenant_ids', $input);
        $primaryProvided = array_key_exists('primary_tenant_id', $input);
        $existing = $this->userReadRepository->find($userId) ?? [];
        if ($themeProvided) {
            $form['theme'] = $this->normalizeTheme($input['theme'] ?? null);
        } else {
            $form['theme'] = $this->normalizeTheme($existing['theme'] ?? null);
        }
        if ($tenantIdsProvided) {
            $tenantIds = $input['tenant_ids'] ?? [];
            if (!is_array($tenantIds)) {
                $tenantIds = [$tenantIds];
            }
            $tenantIds = $this->normalizeTenantIds($tenantIds);
        } else {
            $tenantIds = $this->userAssignmentService->buildAssignmentsForUser($userId)['tenants'] ?? [];
            $tenantIds = array_values(array_map(static fn (array $tenant): int => (int) ($tenant['id'] ?? 0), $tenantIds));
            $tenantIds = array_values(array_filter($tenantIds, static fn (int $id): bool => $id > 0));
        }
        if (array_key_exists('active', $input)) {
            $form['active'] = isset($input['active']) ? 1 : 0;
        } else {
            $form['active'] = (int) ($existing['active'] ?? 1);
        }
        $password = (string) ($input['password'] ?? '');
        $password2 = (string) ($input['password2'] ?? '');

        $errors = $this->validateBase($form, $userId);
        if ($userId === $currentUserId && !$form['active']) {
            $errors[] = t('You cannot deactivate your own account');
        }
        $errors = array_merge($errors, $this->userPasswordService->validatePassword($password, $password2, false, $form['email']));
        if ($tenantIds && ($tenantIdsProvided || $primaryProvided)) {
            [$primaryTenantId, $primaryErrors] = $this->normalizePrimaryTenant($primaryTenantId, $tenantIds);
            $errors = array_merge($errors, $primaryErrors);
        }

        if ($errors) {
            if ($tenantIdsProvided || $primaryProvided) {
                $form['primary_tenant_id'] = $primaryTenantId;
            }
            return ['ok' => false, 'errors' => $errors, 'form' => $form];
        }

        if ($tenantIdsProvided || $primaryProvided) {
            $form['primary_tenant_id'] = $primaryTenantId > 0 ? $primaryTenantId : null;
        }

        $updateData = [
            'first_name' => $form['first_name'],
            'last_name' => $form['last_name'],
            'email' => $form['email'],
            'profile_description' => $form['profile_description'] !== '' ? $form['profile_description'] : null,
            'job_title' => $form['job_title'] !== '' ? $form['job_title'] : null,
            'phone' => $form['phone'] !== '' ? $form['phone'] : null,
            'mobile' => $form['mobile'] !== '' ? $form['mobile'] : null,
            'short_dial' => $form['short_dial'] !== '' ? $form['short_dial'] : null,
            'address' => $form['address'] !== '' ? $form['address'] : null,
            'postal_code' => $form['postal_code'] !== '' ? $form['postal_code'] : null,
            'city' => $form['city'] !== '' ? $form['city'] : null,
            'country' => $form['country'] !== '' ? $form['country'] : null,
            'region' => $form['region'] !== '' ? $form['region'] : null,
            'hire_date' => $form['hire_date'] !== '' ? $form['hire_date'] : null,
            'totp_secret' => $form['totp_secret'],
            'locale' => $form['locale'],
            'active' => $form['active'],
            'password' => $password,
            'modified_by' => $currentUserId > 0 ? $currentUserId : null,
        ];
        if ($themeProvided) {
            $updateData['theme'] = $form['theme'];
        }
        if ($tenantIdsProvided || $primaryProvided) {
            $updateData['primary_tenant_id'] = $form['primary_tenant_id'] ?? null;
        }

        $activeChanged = (int) ($existing['active'] ?? 1) !== (int) $form['active'];
        if ($activeChanged) {
            $updateData['active_changed_at'] = gmdate('Y-m-d H:i:s');
            $updateData['active_changed_by'] = $currentUserId > 0 ? $currentUserId : null;
        }

        $updated = $this->userWriteRepository->update($userId, $updateData);

        if (!$updated) {
            return ['ok' => false, 'errors' => [t('User can not be updated')], 'form' => $form];
        }

        if ($activeChanged) {
            $this->userAssignmentService->bumpAuthzVersion($userId);
        }

        $this->systemAuditService->record('admin.users.update', 'success', [
            'actor_user_id' => $currentUserId > 0 ? $currentUserId : null,
            'target_type' => 'user',
            'target_id' => $userId,
            'target_uuid' => (string) ($existing['uuid'] ?? ''),
            'before' => [
                'active' => $existing['active'] ?? null,
                'locale' => $existing['locale'] ?? null,
                'theme' => $existing['theme'] ?? null,
                'primary_tenant_id' => $existing['primary_tenant_id'] ?? null,
            ],
            'after' => [
                'active' => $form['active'],
                'locale' => $form['locale'],
                'theme' => $form['theme'],
                'primary_tenant_id' => $form['primary_tenant_id'] ?? ($existing['primary_tenant_id'] ?? null),
            ],
        ]);

        return ['ok' => true, 'form' => $form];
    }

    public function setActiveByUuid(string $uuid, bool $active, int $currentUserId = 0): array
    {
        $uuid = trim($uuid);
        if ($uuid === '') {
            return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
        }

        $user = $this->userReadRepository->findByUuid($uuid);
        if (!$user || !isset($user['id'])) {
            return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
        }

        $userId = (int) $user['id'];
        if (!$active && $currentUserId && $currentUserId === $userId) {
            return [
                'ok' => false,
                'status' => 400,
                'error' => 'self_deactivate',
                'message' => t('You cannot deactivate your own account'),
            ];
        }

        $updated = $this->userWriteRepository->setActive($userId, $active, $currentUserId > 0 ? $currentUserId : null);
        if (!$updated) {
            return ['ok' => false, 'status' => 500, 'error' => 'update_failed'];
        }

        $this->userAssignmentService->bumpAuthzVersion($userId);

        $this->systemAuditService->record($active ? 'admin.users.activate' : 'admin.users.deactivate', 'success', [
            'actor_user_id' => $currentUserId > 0 ? $currentUserId : null,
            'target_type' => 'user',
            'target_id' => $userId,
            'target_uuid' => (string) ($user['uuid'] ?? ''),
            'before' => ['active' => $user['active'] ?? null],
            'after' => ['active' => $active ? 1 : 0],
        ]);

        return ['ok' => true, 'user' => $user];
    }

    public function setActiveByUuids(array $uuids, bool $active, int $currentUserId = 0): array
    {
        $uuids = array_values(array_filter(array_map('trim', $uuids)));
        if (!$uuids) {
            return ['ok' => false, 'error' => 'no_selection'];
        }

        if ($currentUserId > 0) {
            $uuids = $this->filterUuidsByTenantScope($uuids, $currentUserId);
            if (!$uuids) {
                return ['ok' => false, 'error' => 'permission_denied'];
            }
        }

        $updated = $this->userWriteRepository->setActiveByUuids($uuids, $active, $currentUserId > 0 ? $currentUserId : null);
        if (!$updated) {
            return ['ok' => false, 'error' => 'update_failed'];
        }

        $userIds = [];
        foreach ($uuids as $uuid) {
            $user = $this->userReadRepository->findByUuid((string) $uuid);
            $userId = (int) ($user['id'] ?? 0);
            if ($userId > 0) {
                $userIds[] = $userId;
            }
        }
        if ($userIds) {
            $this->userWriteRepository->bumpAuthzVersionByUserIds($userIds);
        }

        $this->systemAuditService->record('admin.users.bulk_update', 'success', [
            'actor_user_id' => $currentUserId > 0 ? $currentUserId : null,
            'target_type' => 'user',
            'metadata' => [
                'action' => $active ? 'activate' : 'deactivate',
                'count' => count($uuids),
                'target_uuids' => $uuids,
            ],
        ]);

        return ['ok' => true, 'count' => count($uuids)];
    }

    public function register(array $input): array
    {
        $form = $this->sanitizeBase($input);
        $password = (string) ($input['password'] ?? '');
        $password2 = (string) ($input['password2'] ?? '');

        $errors = $this->validateBase($form);
        $errors = array_merge($errors, $this->userPasswordService->validatePassword($password, $password2, true, $form['email']));

        if ($errors) {
            return ['ok' => false, 'error' => $errors[0]];
        }

        $defaultTheme = $this->settingsGateway->getAppTheme();
        $defaultTenantId = $this->settingsGateway->getDefaultTenantId();
        $activeChangedAt = gmdate('Y-m-d H:i:s');

        $transactionStarted = false;
        try {
            $this->databaseSessionRepository->beginTransaction();
            $transactionStarted = true;

            $created = $this->userWriteRepository->create([
                'first_name' => $form['first_name'],
                'last_name' => $form['last_name'],
                'email' => $form['email'],
                'password' => $password,
                'locale' => I18n::$locale,
                'totp_secret' => '',
                'theme' => $this->normalizeTheme($defaultTheme ?? 'light'),
                'primary_tenant_id' => $defaultTenantId ?: null,
                'active' => 1,
                'created_by' => null,
                'active_changed_at' => $activeChangedAt,
                'active_changed_by' => null,
            ]);

            if (!$created) {
                $this->rollbackQuietly();
                return ['ok' => false, 'error' => t('User can not be registered')];
            }

            $createdUser = $this->userReadRepository->findByEmail($form['email']);
            $userId = (int) ($createdUser['id'] ?? 0);
            if ($userId <= 0) {
                $this->rollbackQuietly();
                return ['ok' => false, 'error' => t('User can not be registered')];
            }

            if ($defaultTenantId && !$this->userAssignmentService->syncTenants($userId, [$defaultTenantId])) {
                $this->rollbackQuietly();
                return ['ok' => false, 'error' => t('User can not be registered')];
            }
            $defaultRoleId = $this->settingsGateway->getDefaultRoleId();
            if ($defaultRoleId && !$this->userAssignmentService->syncRoles($userId, [$defaultRoleId])) {
                $this->rollbackQuietly();
                return ['ok' => false, 'error' => t('User can not be registered')];
            }
            $defaultDepartmentId = $this->settingsGateway->getDefaultDepartmentId();
            if ($defaultDepartmentId && !$this->userAssignmentService->syncDepartments($userId, [$defaultDepartmentId])) {
                $this->rollbackQuietly();
                return ['ok' => false, 'error' => t('User can not be registered')];
            }

            $this->databaseSessionRepository->commitTransaction();
            $transactionStarted = false;
        } catch (\Throwable $exception) {
            if ($transactionStarted) {
                $this->rollbackQuietly();
            }
            return ['ok' => false, 'error' => t('User can not be registered')];
        }

        return ['ok' => true];
    }

    /**
     * Self-service profile update (subset of fields, no admin-only changes).
     *
     * @param int   $userId
     * @param array $input
     * @return array{ok: bool, errors?: list<string>, form?: array<string, mixed>}
     */
    public function updateSelfProfile(int $userId, array $input): array
    {
        $existing = $this->userReadRepository->find($userId);
        if (!$existing) {
            return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
        }

        $allowedFields = [
            'first_name', 'last_name', 'profile_description', 'job_title',
            'phone', 'mobile', 'short_dial',
            'address', 'postal_code', 'city', 'country', 'region',
        ];

        $form = [];
        foreach ($allowedFields as $field) {
            $form[$field] = array_key_exists($field, $input)
                ? trim((string) $input[$field])
                : ($existing[$field] ?? '');
        }

        $errors = [];
        if ($form['first_name'] === '') {
            $errors[] = t('First name cannot be empty');
        }
        if ($form['last_name'] === '') {
            $errors[] = t('Last name cannot be empty');
        }

        $locale = array_key_exists('locale', $input)
            ? $this->normalizeLocale($input['locale'])
            : ($existing['locale'] ?? '');
        $theme = array_key_exists('theme', $input)
            ? $this->normalizeTheme($input['theme'])
            : ($existing['theme'] ?? 'light');

        // Tenant switch
        $currentTenantId = null;
        if (array_key_exists('current_tenant_uuid', $input)) {
            $tenantUuid = trim((string) ($input['current_tenant_uuid'] ?? ''));
            if ($tenantUuid !== '') {
                $tenant = $this->directoryGateway->findTenantByUuid($tenantUuid);
                if (!$tenant || !isset($tenant['id'])) {
                    $errors[] = t('Tenant not found');
                } else {
                    $userTenantIds = $this->userAssignmentService->buildAssignmentsForUser($userId)['tenants'] ?? [];
                    $userTenantIds = array_map(static fn (array $t): int => (int) ($t['id'] ?? 0), $userTenantIds);
                    $tenantId = (int) $tenant['id'];
                    if (!in_array($tenantId, $userTenantIds, true)) {
                        $errors[] = t('No access to this tenant');
                    } else {
                        $currentTenantId = $tenantId;
                    }
                }
            }
        }

        if ($errors) {
            return ['ok' => false, 'errors' => $errors, 'form' => $form];
        }

        $updateData = [
            'first_name' => $form['first_name'],
            'last_name' => $form['last_name'],
            'profile_description' => $form['profile_description'] !== '' ? $form['profile_description'] : null,
            'job_title' => $form['job_title'] !== '' ? $form['job_title'] : null,
            'phone' => $form['phone'] !== '' ? $form['phone'] : null,
            'mobile' => $form['mobile'] !== '' ? $form['mobile'] : null,
            'short_dial' => $form['short_dial'] !== '' ? $form['short_dial'] : null,
            'address' => $form['address'] !== '' ? $form['address'] : null,
            'postal_code' => $form['postal_code'] !== '' ? $form['postal_code'] : null,
            'city' => $form['city'] !== '' ? $form['city'] : null,
            'country' => $form['country'] !== '' ? $form['country'] : null,
            'region' => $form['region'] !== '' ? $form['region'] : null,
            'locale' => $locale,
            'theme' => $theme,
            'modified_by' => $userId,
        ];

        $updated = $this->userWriteRepository->update($userId, $updateData);
        if (!$updated) {
            return ['ok' => false, 'errors' => [t('Profile can not be updated')]];
        }

        if ($currentTenantId !== null) {
            $this->userWriteRepository->setCurrentTenant($userId, $currentTenantId);
        }

        return ['ok' => true, 'form' => $form];
    }

    private function filterUuidsByTenantScope(array $uuids, int $currentUserId): array
    {
        $allowed = [];
        foreach ($uuids as $uuid) {
            $user = $this->userReadRepository->findByUuid((string) $uuid);
            if (!$user || !isset($user['id'])) {
                continue;
            }
            $userId = (int) $user['id'];
            if ($userId === $currentUserId) {
                $allowed[] = (string) $uuid;
                continue;
            }
            if ($this->scopeGateway->canAccess('users', $userId, $currentUserId)) {
                $allowed[] = (string) $uuid;
            }
        }
        return array_values(array_unique($allowed));
    }

    private function sanitizeBase(array $input): array
    {
        return [
            'first_name' => trim((string) ($input['first_name'] ?? '')),
            'last_name' => trim((string) ($input['last_name'] ?? '')),
            'email' => trim((string) ($input['email'] ?? '')),
            'profile_description' => trim((string) ($input['profile_description'] ?? '')),
            'job_title' => trim((string) ($input['job_title'] ?? '')),
            'phone' => trim((string) ($input['phone'] ?? '')),
            'mobile' => trim((string) ($input['mobile'] ?? '')),
            'short_dial' => trim((string) ($input['short_dial'] ?? '')),
            'address' => trim((string) ($input['address'] ?? '')),
            'postal_code' => trim((string) ($input['postal_code'] ?? '')),
            'city' => trim((string) ($input['city'] ?? '')),
            'country' => trim((string) ($input['country'] ?? '')),
            'region' => trim((string) ($input['region'] ?? '')),
            'hire_date' => trim((string) ($input['hire_date'] ?? '')),
        ];
    }

    private function rollbackQuietly(): void
    {
        try {
            $this->databaseSessionRepository->rollbackTransaction();
        } catch (\Throwable) {
            // Swallow — the original exception is more important.
        }
    }

    private function normalizeTheme($value): string
    {
        $theme = strtolower(trim((string) $value));
        $themes = function_exists('appThemes') ? appThemes() : ['light' => 'Light', 'dark' => 'Dark'];
        if ($theme === '' || !isset($themes[$theme])) {
            return function_exists('appDefaultTheme') ? appDefaultTheme() : 'light';
        }
        return $theme;
    }

    private function normalizeLocale($value): string
    {
        $locale = strtolower(trim((string) $value));
        $available = defined('APP_LOCALES') ? APP_LOCALES : [I18n::$defaultLocale];
        if ($locale === '' || !in_array($locale, $available, true)) {
            return I18n::$locale ?? I18n::$defaultLocale;
        }
        return $locale;
    }

    private function validateBase(array $form, ?int $excludeId = null): array
    {
        $errors = [];
        if ($form['first_name'] === '') {
            $errors[] = t('First name cannot be empty');
        }
        if ($form['last_name'] === '') {
            $errors[] = t('Last name cannot be empty');
        }
        if ($form['email'] === '') {
            $errors[] = t('Email cannot be empty');
        } elseif (!filter_var($form['email'], FILTER_VALIDATE_EMAIL)) {
            $errors[] = t('Email is not valid');
        } else {
            $existing = $this->userReadRepository->findByEmail($form['email']);
            if ($existing && (!isset($existing['id']) || (int) $existing['id'] !== (int) $excludeId)) {
                $errors[] = t('Email is already taken');
            }
        }

        return $errors;
    }

    private function normalizePrimaryTenant(int $primaryTenantId, array $tenantIds): array
    {
        if (!$tenantIds) {
            return [0, []];
        }
        if ($primaryTenantId > 0 && !in_array($primaryTenantId, $tenantIds, true)) {
            return [$primaryTenantId, [t('Primary tenant must be one of the assigned tenants')]];
        }
        if ($primaryTenantId === 0 && count($tenantIds) > 1) {
            return [0, [t('Please select a primary tenant')]];
        }
        // Single tenant assigned → auto-select it as primary to avoid requiring the user to pick.
        if ($primaryTenantId === 0 && count($tenantIds) === 1) {
            return [(int) $tenantIds[0], []];
        }
        return [$primaryTenantId, []];
    }

    private function normalizeTenantIds(array $tenantIds): array
    {
        return $this->userAssignmentService->normalizeTenantIds($tenantIds);
    }
}
