<?php

use MintyPHP\Buffer;
use MintyPHP\DB;
use MintyPHP\I18n;
use MintyPHP\Repository\Auth\PasswordResetRepository;
use MintyPHP\Repository\Auth\RememberTokenRepository;
use MintyPHP\Router;
use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Auth\AuthServicesFactory;
use MintyPHP\Service\CustomField\UserCustomFieldValueService;
use MintyPHP\Service\User\UserServicesFactory;
use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard;

Guard::requireLogin();
$userServicesFactory = new UserServicesFactory();
$userAccountService = $userServicesFactory->createUserAccountService();
$userAssignmentService = $userServicesFactory->createUserAssignmentService();
$userTenantRepository = $userServicesFactory->createUserTenantRepository();
$userRoleRepository = $userServicesFactory->createUserRoleRepository();
$userDepartmentRepository = $userServicesFactory->createUserDepartmentRepository();
$userPasswordPolicyService = $userServicesFactory->createUserPasswordPolicyService();
$authServiceFactory = new AuthServicesFactory();
$accessServicesFactory = new AccessServicesFactory();
$rolePermissionRepository = $accessServicesFactory->createRolePermissionRepository();
$authService = $authServiceFactory->createAuthService();
$apiTokenService = $authServiceFactory->createApiTokenService();
$passwordResetRepository = new PasswordResetRepository();
$rememberTokenRepository = new RememberTokenRepository();
$passwordMinLength = $userPasswordPolicyService->minLength();
$passwordHints = $userPasswordPolicyService->hints();

$currentUserId = (int) ($_SESSION['user']['id'] ?? 0);
if ($currentUserId > 0) {
    permissionGateway()->getUserPermissions($currentUserId, true);
}

$uuid = trim((string) ($id ?? ''));
$user = $uuid !== '' ? $userAccountService->findByUuid($uuid) : null;
if (!$user) {
    Flash::error('User not found', 'admin/users', 'user_not_found');
    Router::redirect('admin/users');
}

$userId = (int) ($user['id'] ?? 0);
$canViewUsers = permissionGateway()->userHas($currentUserId, PermissionService::USERS_VIEW);
$canUpdateUser = permissionGateway()->userHas($currentUserId, PermissionService::USERS_UPDATE);
$canUpdateSelf = permissionGateway()->userHas($currentUserId, PermissionService::USERS_SELF_UPDATE);
$canUpdateAssignments = permissionGateway()->userHas($currentUserId, PermissionService::USERS_UPDATE_ASSIGNMENTS);
$isOwnAccount = $currentUserId === $userId;
$canEditUser = $isOwnAccount ? ($canUpdateUser || $canUpdateSelf) : $canUpdateUser;
$canEditAssignments = $canEditUser && $canUpdateAssignments;
if (!$isOwnAccount && !directoryServicesFactory()->createDirectoryScopeGateway()->canAccess('users', $userId, $currentUserId)) {
    Router::redirect('error/forbidden');
    return;
}
if (!$canViewUsers && !$canEditUser) {
    Router::redirect('error/forbidden');
    return;
}
$creatorId = (int) ($user['created_by'] ?? 0);
if ($creatorId > 0) {
    $creator = $userAccountService->findById($creatorId);
    if ($creator) {
        $creatorName = trim(($creator['first_name'] ?? '') . ' ' . ($creator['last_name'] ?? ''));
        $user['created_by_label'] = $creatorName !== '' ? $creatorName : ($creator['email'] ?? '');
        $user['created_by_uuid'] = $creator['uuid'] ?? null;
    }
}
$modifierId = (int) ($user['modified_by'] ?? 0);
if ($modifierId > 0) {
    $modifier = $userAccountService->findById($modifierId);
    if ($modifier) {
        $modifierName = trim(($modifier['first_name'] ?? '') . ' ' . ($modifier['last_name'] ?? ''));
        $user['modified_by_label'] = $modifierName !== '' ? $modifierName : ($modifier['email'] ?? '');
        $user['modified_by_uuid'] = $modifier['uuid'] ?? null;
    }
}
$activeChangedById = (int) ($user['active_changed_by'] ?? 0);
if ($activeChangedById > 0) {
    $activeChanger = $userAccountService->findById($activeChangedById);
    if ($activeChanger) {
        $activeChangerName = trim(($activeChanger['first_name'] ?? '') . ' ' . ($activeChanger['last_name'] ?? ''));
        $user['active_changed_by_label'] = $activeChangerName !== '' ? $activeChangerName : ($activeChanger['email'] ?? '');
        $user['active_changed_by_uuid'] = $activeChanger['uuid'] ?? null;
    }
}
$errors = [];
$form = $user;
// Users with tenants.update permission can see ALL tenants (to assign new tenants to users)
$canManageTenants = permissionGateway()->userHas($currentUserId, PermissionService::TENANTS_UPDATE);
$allowedTenantIds = $canManageTenants ? null : directoryServicesFactory()->createDirectoryScopeGateway()->getUserTenantIds($currentUserId);

$tenants = directoryServicesFactory()->createTenantService()->list();
if ($allowedTenantIds) {
    $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool {
        $tenantId = (int) ($tenant['id'] ?? 0);
        return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true);
    }));
} elseif (!$canManageTenants && directoryServicesFactory()->createDirectoryScopeGateway()->isStrict()) {
    $tenants = [];
}
$selectedTenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
if ($allowedTenantIds) {
    $selectedTenantIds = array_values(array_intersect($selectedTenantIds, $allowedTenantIds));
}
$primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0);
if (!$primaryTenantId && count($selectedTenantIds) === 1) {
    $primaryTenantId = (int) $selectedTenantIds[0];
    $user['primary_tenant_id'] = $primaryTenantId;
}
$roles = directoryServicesFactory()->createRoleService()->listActive();
$selectedRoleIds = $userRoleRepository->listRoleIdsByUserId($userId);
$permissionRows = $rolePermissionRepository->listPermissionsWithRolesByRoleIds($selectedRoleIds);
$selectedDepartmentIds = $userDepartmentRepository->listDepartmentIdsByUserId($userId);
$departmentOptionsByTenant = directoryServicesFactory()->createDepartmentService()->groupActiveByTenantIds($selectedTenantIds);
$canEditCustomFieldValues = $canEditUser
    && (permissionGateway()->userHas($currentUserId, PermissionService::CUSTOM_FIELDS_EDIT_VALUES)
        || ($isOwnAccount && $canUpdateSelf));
$customFieldDefinitionsByTenant = UserCustomFieldValueService::buildDefinitionsByTenant($selectedTenantIds);
$customFieldDefinitionIds = [];
foreach ($customFieldDefinitionsByTenant as $tenantDefinitions) {
    foreach ($tenantDefinitions as $definition) {
        $definitionId = (int) ($definition['id'] ?? 0);
        if ($definitionId > 0) {
            $customFieldDefinitionIds[] = $definitionId;
        }
    }
}
$customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds);
$customFieldPostedValues = [
    'custom_field_values' => [],
    'custom_field_values_multi' => [],
];
$passwordResets = [];
if ($canViewUsers || $canEditUser) {
    $passwordResets = $passwordResetRepository->listByUserId($userId, 25);
}
$rememberTokens = [];
if ($canViewUsers || $canEditUser) {
    $rememberTokens = $rememberTokenRepository->listByUserId($userId, 25);
}
$apiTokens = [];
$canManageApiTokens = $canEditUser
    && permissionGateway()->userHas($currentUserId, PermissionService::API_TOKENS_MANAGE);
if ($canViewUsers || $canEditUser) {
    $apiTokens = $apiTokenService->listForUser($userId);
}
$showApiTokens = !empty($apiTokens) || $canManageApiTokens;
// Keep initial $isOwnAccount/$canEditUser values for view permissions.

if (isset($_POST['email'])) {
    if (!$canEditUser) {
        Router::redirect('error/forbidden');
        return;
    }
    $result = $userAccountService->updateFromAdmin($userId, $_POST, $currentUserId);
    $form = $result['form'] ?? $form;
    $errors = $result['errors'] ?? [];
    $tenantIds = $userAssignmentService->normalizeIdInput($_POST['tenant_ids'] ?? []);
    $existingTenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
    $tenantIdsForSync = $tenantIds;
    if ($canEditAssignments && $allowedTenantIds) {
        $tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds));
        $tenantIdsForSync = directoryServicesFactory()->createDirectoryScopeGateway()->mergeTenantIdsPreservingOutOfScope(
            $tenantIds,
            $existingTenantIds,
            $allowedTenantIds
        );
    } elseif ($canEditAssignments && !$canManageTenants && directoryServicesFactory()->createDirectoryScopeGateway()->isStrict()) {
        $tenantIds = [];
        $tenantIdsForSync = $existingTenantIds;
    }
    if (!$canEditAssignments) {
        $tenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
        if ($allowedTenantIds) {
            $tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds));
        }
        $tenantIdsForSync = $tenantIds;
    }
    $selectedTenantIds = $tenantIds;
    $primaryTenantId = (int) ($_POST['primary_tenant_id'] ?? 0);
    $selectedRoleIds = $userAssignmentService->normalizeIdInput($_POST['role_ids'] ?? []);
    $selectedDepartmentIds = $userAssignmentService->normalizeIdInput($_POST['department_ids'] ?? []);
    $departmentOptionsByTenant = directoryServicesFactory()->createDepartmentService()->groupActiveByTenantIds($selectedTenantIds);
    $customFieldDefinitionsByTenant = UserCustomFieldValueService::buildDefinitionsByTenant($selectedTenantIds);
    $customFieldDefinitionIds = [];
    foreach ($customFieldDefinitionsByTenant as $tenantDefinitions) {
        foreach ($tenantDefinitions as $definition) {
            $definitionId = (int) ($definition['id'] ?? 0);
            if ($definitionId > 0) {
                $customFieldDefinitionIds[] = $definitionId;
            }
        }
    }
    $customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds);
    $customFieldPostedValues = [
        'custom_field_values' => is_array($_POST['custom_field_values'] ?? null) ? $_POST['custom_field_values'] : [],
        'custom_field_values_multi' => is_array($_POST['custom_field_values_multi'] ?? null) ? $_POST['custom_field_values_multi'] : [],
    ];

    if ($result['ok'] ?? false) {
        if ($canEditAssignments) {
            $db = DB::handle();
            $db->begin_transaction();
            try {
                $userAssignmentService->syncTenants($userId, $tenantIdsForSync, false);
                $userAssignmentService->syncRoles($userId, $selectedRoleIds, false);
                $userAssignmentService->syncDepartments($userId, $selectedDepartmentIds, false);
                $userAssignmentService->bumpAuthzVersion($userId);
                $db->commit();
            } catch (\Throwable $exception) {
                $db->rollback();
                $errors[] = t('User assignments can not be updated');
            }

            // Update session if editing own account (tenant assignments changed)
            if ($currentUserId === $userId && !$errors) {
                $authService->loadTenantDataIntoSession($userId);
            }
        }
        $customFieldSyncResult = UserCustomFieldValueService::syncForUser(
            $userId,
            $selectedTenantIds,
            $_POST,
            $canEditCustomFieldValues
        );
        if (!($customFieldSyncResult['ok'] ?? false)) {
            $errors = array_merge($errors, $customFieldSyncResult['errors'] ?? []);
            $customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds);
        }
        if ($currentUserId === $userId && isset($form['theme'])) {
            $themes = appThemes();
            $themeValue = strtolower(trim((string) ($form['theme'] ?? '')));
            $_SESSION['user']['theme'] = isset($themes[$themeValue]) ? $themeValue : appDefaultTheme();
        }
        if ($currentUserId === $userId && isset($form['locale'])) {
            $_SESSION['user']['locale'] = $form['locale'];
            I18n::$locale = $form['locale'];
        }
        if (!$errors) {
            $action = (string) ($_POST['action'] ?? 'save');
            if ($action === 'save_close') {
                Flash::success('User updated', 'admin/users', 'user_updated');
                Router::redirect('admin/users');
            } else {
                Flash::success('User updated', "admin/users/edit/{$uuid}", 'user_updated');
                Router::redirect("admin/users/edit/{$uuid}");
            }
        }
    }
}

Buffer::set('title', $isOwnAccount ? t('My account') : t('Edit user'));
