<?php

use MintyPHP\Buffer;
use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\I18n;
use MintyPHP\Repository\Auth\PasswordResetRepository;
use MintyPHP\Repository\Auth\RememberTokenRepository;
use MintyPHP\Router;
use MintyPHP\Service\Access\UserAuthorizationPolicy;
use MintyPHP\Service\CustomField\UserCustomFieldValueService;
use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard;

$sessionStore = app(SessionStoreInterface::class);
$session = $sessionStore->all();

Guard::requireLogin();
$request = requestInput();
$returnTarget = requestResolveReturnTarget();
$closeTarget = requestResolveReturnTarget('admin/users');
$userAccountService = app(\MintyPHP\Service\User\UserAccountService::class);
$userAssignmentService = app(\MintyPHP\Service\User\UserAssignmentService::class);
$userTenantRepository = app(\MintyPHP\Repository\Tenant\UserTenantRepository::class);
$userRoleRepository = app(\MintyPHP\Repository\Access\UserRoleRepository::class);
$userDepartmentRepository = app(\MintyPHP\Repository\Org\UserDepartmentRepository::class);
$userPasswordPolicyService = app(\MintyPHP\Service\User\UserPasswordPolicyService::class);
$userCustomFieldValueService = app(UserCustomFieldValueService::class);
$authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class);
$rolePermissionRepository = app(\MintyPHP\Repository\Access\RolePermissionRepository::class);
$authService = app(\MintyPHP\Service\Auth\AuthService::class);
$apiTokenService = app(\MintyPHP\Service\Auth\ApiTokenService::class);
$passwordResetRepository = app(PasswordResetRepository::class);
$rememberTokenRepository = app(RememberTokenRepository::class);
$tenantService = app(\MintyPHP\Service\Tenant\TenantService::class);
$roleService = app(\MintyPHP\Service\Access\RoleService::class);
$departmentService = app(\MintyPHP\Service\Org\DepartmentService::class);
$passwordMinLength = $userPasswordPolicyService->minLength();
$passwordHints = $userPasswordPolicyService->hints();

$currentUserId = (int) ($session['user']['id'] ?? 0);
$uuid = trim((string) ($id ?? ''));
$editTarget = requestPathWithReturnTarget("admin/users/edit/{$uuid}", $returnTarget);

// Resolve the user BEFORE the aggregator so the authorize-context can carry
// the real target_user_id (matches today's order: lookup -> CONTEXT authorize
// -> Router::redirect on forbidden). The aggregator receives a trivial finder
// that returns the already-loaded model so its not-found branch fires only
// when findByUuid() returned null.
$user = $uuid !== '' ? $userAccountService->findByUuid($uuid) : null;
$userId = (int) ($user['id'] ?? 0);

$context = actionEditContext([
    'finder' => static fn (string $_id): mixed => $user,
    'rawId' => $uuid,
    'notFoundFlashKey' => 'User not found',
    'notFoundRedirectPath' => $closeTarget,
    'notFoundFlashScopeKey' => 'user_not_found',
    'abilityKey' => UserAuthorizationPolicy::ABILITY_ADMIN_USERS_EDIT_CONTEXT,
    'context' => [
        'actor_user_id' => $currentUserId,
        'target_user_id' => $userId,
    ],
    'viewAuthFlags' => [
        'can_view_users',
        'can_view_page',
        'can_edit_user',
        'can_edit_assignments',
        'can_manage_tenants',
        'can_edit_custom_field_values',
        'can_manage_api_tokens',
        'can_view_user_meta',
        'can_view_user_audit',
        'can_access_pdf',
        'can_delete_user',
        'can_view_security_artifacts',
        'can_view_permissions_table',
        'is_own_account',
    ],
    'forbiddenStrategy' => 'redirect',
    'tenantScopeFlagKey' => 'can_manage_tenants',
]);
$user = is_array($context['model']) ? $context['model'] : $user;
$capabilities = $context['capabilities'];
$tenantScope = $context['tenantScope'];
$viewAuth = $context['viewAuth'];

$canViewUsers = (bool) ($capabilities['can_view_users'] ?? false);
$canViewPage = (bool) ($capabilities['can_view_page'] ?? false);
$canEditUser = (bool) ($capabilities['can_edit_user'] ?? false);
$canEditAssignments = (bool) ($capabilities['can_edit_assignments'] ?? false);
$canManageTenants = (bool) ($capabilities['can_manage_tenants'] ?? false);
$canEditCustomFieldValues = (bool) ($capabilities['can_edit_custom_field_values'] ?? false);
$canManageApiTokens = (bool) ($capabilities['can_manage_api_tokens'] ?? false);
$canViewUserMeta = (bool) ($capabilities['can_view_user_meta'] ?? false);
$canViewUserAudit = (bool) ($capabilities['can_view_user_audit'] ?? false);
$canAccessPdf = (bool) ($capabilities['can_access_pdf'] ?? false);
$canDeleteUser = (bool) ($capabilities['can_delete_user'] ?? false);
$canViewSecurityArtifacts = (bool) ($capabilities['can_view_security_artifacts'] ?? false);
$canViewPermissionsTable = (bool) ($capabilities['can_view_permissions_table'] ?? false);
$isOwnAccount = (bool) ($capabilities['is_own_account'] ?? false);
// $canManageAllTenants = Tupel-Diskriminator (true wenn Aggregator scope='all'
// liefert via tenantScopeFlagKey='can_manage_tenants'-Override). $canManageTenants
// bleibt als Capability-Flag erhalten — semantisch aequivalent, aber unterschiedliche
// Quelle. Filter-Branches unten nutzen $canManageAllTenants als Tupel-Diskriminator.
$canManageAllTenants = $tenantScope['scope'] === 'all';

if ($canViewUserMeta || $canViewUserAudit) {
    app(\MintyPHP\Service\Audit\AuditMetadataEnricherInterface::class)->enrich($user, ['created_by', 'modified_by', 'active_changed_by']);
}

$errorBag = formErrors();
$errors = [];
$form = $user;
$scopeGateway = app(\MintyPHP\Service\Tenant\TenantScopeService::class);
$strictTenantScope = $scopeGateway->isStrict();

$tenants = $tenantService->list();
if (!$canManageAllTenants) {
    if (count($tenantScope['ids']) > 0) {
        $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($tenantScope): bool {
            $tenantId = (int) ($tenant['id'] ?? 0);
            return $tenantId > 0 && in_array($tenantId, $tenantScope['ids'], true);
        }));
    } elseif ($strictTenantScope) {
        $tenants = [];
    }
}

$selectedTenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
if (!$canManageAllTenants && count($tenantScope['ids']) > 0) {
    $selectedTenantIds = array_values(array_intersect($selectedTenantIds, $tenantScope['ids']));
}
$primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0);
if (!$primaryTenantId && count($selectedTenantIds) === 1) {
    $primaryTenantId = (int) $selectedTenantIds[0];
    $user['primary_tenant_id'] = $primaryTenantId;
}

$roles = $roleService->listActive();
$assignableRoleService = app(\MintyPHP\Service\Access\AssignableRoleService::class);
$assignableRoleIds = $assignableRoleService->listAssignableRoleIdsForActor($currentUserId);
$assignableRoleIdMap = array_fill_keys($assignableRoleIds, true);
$roles = array_values(array_filter($roles, static fn (array $r): bool => isset($assignableRoleIdMap[(int) ($r['id'] ?? 0)])));
$selectedRoleIds = $userRoleRepository->listRoleIdsByUserId($userId);
$visibleSelectedRoleIds = array_values(array_intersect($selectedRoleIds, $assignableRoleIds));
$permissionRows = $canViewPermissionsTable
    ? $rolePermissionRepository->listPermissionsWithRolesByRoleIds($selectedRoleIds)
    : [];
$selectedDepartmentIds = $userDepartmentRepository->listDepartmentIdsByUserId($userId);
$departmentOptionsByTenant = $departmentService->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' => [],
    'custom_field_values_multi' => [],
];

$passwordResets = [];
$rememberTokens = [];
$apiTokens = [];
if ($canViewSecurityArtifacts) {
    $passwordResets = $passwordResetRepository->listByUserId($userId, 25);
    $rememberTokens = $rememberTokenRepository->listByUserId($userId, 25);
    $apiTokens = $apiTokenService->listForUser($userId);
}
$canManageApiTokens = $canManageApiTokens && $canViewSecurityArtifacts;
$showApiTokens = $canViewSecurityArtifacts;

if ($request->isMethod('POST') && !actionRequireCsrf($editTarget, $editTarget, 'csrf_expired')) {
    return;
}

if ($request->isMethod('POST')) {
    $post = $request->bodyAll();
    $submitDecision = $authorizationService->authorize(UserAuthorizationPolicy::ABILITY_ADMIN_USERS_EDIT_SUBMIT, [
        'actor_user_id' => $currentUserId,
        'target_user_id' => $userId,
        'input' => $post,
    ]);
    if (!$submitDecision->isAllowed()) {
        Router::redirect('error/forbidden');
        return;
    }

    $submitCapabilities = $submitDecision->attribute('capabilities', []);
    if (!is_array($submitCapabilities) || !isset($submitCapabilities['can_edit_user']) || !$submitCapabilities['can_edit_user']) {
        Router::redirect('error/forbidden');
        return;
    }

    $canEditUser = (bool) $submitCapabilities['can_edit_user'];
    $canEditAssignments = (bool) ($submitCapabilities['can_edit_assignments'] ?? $canEditAssignments);
    $canEditCustomFieldValues = (bool) ($submitCapabilities['can_edit_custom_field_values'] ?? $canEditCustomFieldValues);

    $result = $userAccountService->updateFromAdmin($userId, $post, $currentUserId);
    $form = $result['form'] ?? $form;
    $errorBag->merge($result['errors'] ?? []);
    $tenantIds = $userAssignmentService->normalizeIdInput($post['tenant_ids'] ?? []);
    $existingTenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
    $tenantIdsForSync = $tenantIds;

    if ($canEditAssignments && !$canManageAllTenants && count($tenantScope['ids']) > 0) {
        $tenantIds = array_values(array_intersect($tenantIds, $tenantScope['ids']));
        $tenantIdsForSync = $scopeGateway->mergeTenantIdsPreservingOutOfScope(
            $tenantIds,
            $existingTenantIds,
            $tenantScope['ids']
        );
    } elseif ($canEditAssignments && !$canManageAllTenants && $strictTenantScope) {
        $tenantIds = [];
        $tenantIdsForSync = $existingTenantIds;
    }

    if (!$canEditAssignments) {
        $tenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
        if (!$canManageAllTenants && count($tenantScope['ids']) > 0) {
            $tenantIds = array_values(array_intersect($tenantIds, $tenantScope['ids']));
        }
        $tenantIdsForSync = $tenantIds;
    }

    $selectedTenantIds = $tenantIds;
    $primaryTenantId = (int) ($post['primary_tenant_id'] ?? 0);
    $selectedRoleIds = $userAssignmentService->normalizeIdInput($post['role_ids'] ?? []);
    $selectedDepartmentIds = $userAssignmentService->normalizeIdInput($post['department_ids'] ?? []);
    $departmentOptionsByTenant = $departmentService->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) {
            $assignmentsBefore = $userAssignmentService->buildAssignmentsForUser($userId);
            $assignmentsSynced = $userAssignmentService->syncAllAssignments(
                $userId,
                $tenantIdsForSync,
                $selectedRoleIds,
                $selectedDepartmentIds,
                $currentUserId
            );
            if (!$assignmentsSynced) {
                $errorBag->addGlobal(t('User assignments can not be updated'));
            } else {
                $assignmentsAfter = $userAssignmentService->buildAssignmentsForUser($userId);
                $userAccountService->dispatchAssignmentChangedIfNeeded(
                    $userId,
                    $assignmentsBefore,
                    $assignmentsAfter,
                    $currentUserId
                );
            }

            if ($currentUserId === $userId && !$errorBag->hasAny()) {
                $authService->loadTenantDataIntoSession($userId);
            }
        }

        $customFieldSyncResult = $userCustomFieldValueService->syncForUser(
            $userId,
            $selectedTenantIds,
            $post,
            $canEditCustomFieldValues
        );
        if (!($customFieldSyncResult['ok'] ?? false)) {
            $errorBag->merge($customFieldSyncResult['errors'] ?? []);
            $customFieldValueMap = $userCustomFieldValueService->buildUserValueMap($userId, $customFieldDefinitionIds);
        }
        if ($currentUserId === $userId && isset($form['theme'])) {
            $themes = appThemes();
            $themeValue = strtolower(trim((string) ($form['theme'] ?? '')));
            $userSession = $sessionStore->get('user', []);
            if (is_array($userSession)) {
                $userSession['theme'] = isset($themes[$themeValue]) ? $themeValue : appDefaultTheme();
                $sessionStore->set('user', $userSession);
            }
        }
        if ($currentUserId === $userId && isset($form['locale'])) {
            $userSession = $sessionStore->get('user', []);
            if (is_array($userSession)) {
                $userSession['locale'] = $form['locale'];
                $sessionStore->set('user', $userSession);
            }
            I18n::$locale = $form['locale'];
        }
        if (!$errorBag->hasAny()) {
            $action = (string) ($post['action'] ?? 'save');
            if ($action === 'save_close') {
                Flash::success(t('User updated'), $closeTarget, 'user_updated');
                Router::redirect($closeTarget);
            } else {
                Flash::success(t('User updated'), $editTarget, 'user_updated');
                Router::redirect($editTarget);
            }
        }
    }
}

$validationSummaryErrors = $errorBag->toArray();
$errors = $errorBag->toFlatList();
$titleText = $isOwnAccount ? t('My account') : ($canEditUser ? t('Edit user') : t('View user'));
Buffer::set('title', $titleText);
if (!($isOwnAccount && !$canViewUsers)) {
    $breadcrumbs = [
        ['label' => t('Home'), 'path' => 'admin'],
        ['label' => t('Users'), 'path' => 'admin/users'],
        ['label' => $titleText],
    ];
}
