<?php

namespace routes;

use classes\authentication;
use classes\economic;
use classes\email;
use classes\gatewayapi;
use classes\response;
use classes\subuser_contact_verification_service;
use classes\subusers_schema_bootstrap;
use classes\subuser_permission_templates_service;
use classes\virkdata;
use Exception;
use modules\virkdata\helpers\virkdata_response;
use objects\customer_vehicles_o;
use objects\logs_o;
use objects\subuser_grants_o;
use objects\subusers_o;
use objects\users_o;
use modules\subusers\permissions\subusers_permission_nodes_bookings;
use modules\subusers\permissions\subusers_permission_nodes_orders;
use modules\subusers\permissions\subusers_permission_nodes_selfserve;
use modules\subusers\permissions\subusers_permission_nodes_subusers;
use modules\subusers\permissions\subusers_permission_nodes_vehicles;
use modules\subusers\helpers\subusers_permission_node_key;
use traits\route_t;

class subusersRoute
{
    use route_t;

    private const DOGNVASK_PERMISSION_KEYS = ['SELFSERVE_LIST', 'SELFSERVE_ADD'];

    private function getOwnPermissionForNode(subusers_permission_node_key $node)
    {
        return match ($node) {
            subusers_permission_node_key::SUBUSERS_LIST => self::definePermission('list_own_subusers', subusers_permission_node_key::SUBUSERS_LIST),
            subusers_permission_node_key::SUBUSERS_ADD => self::definePermission('add_own_subusers', subusers_permission_node_key::SUBUSERS_ADD),
            subusers_permission_node_key::SUBUSERS_EDIT => self::definePermission('edit_own_subusers', subusers_permission_node_key::SUBUSERS_EDIT),
            subusers_permission_node_key::SUBUSERS_DELETE => self::definePermission('delete_own_subusers', subusers_permission_node_key::SUBUSERS_DELETE),
            default => self::definePermission(strtolower($node->name), $node),
        };
    }

    private static function requireRegex(string $input, string $regex, string $error_message): void
    {
        if (!preg_match($regex, $input)) {
            global $response;
            $response->error($error_message, 400);
        }
    }

    private function requireSubuserPasswordPolicy(string $password): void
    {
        self::requireType($password, self::type_string());
        self::requireMinLength('password', subusers_o::PASSWORD_MIN_LENGTH);
        self::requireMaxLength('password', subusers_o::PASSWORD_MAX_LENGTH);
        self::requireRegex($password, subusers_o::PASSWORD_PATTERN, subusers_o::PASSWORD_COMPLEXITY_MESSAGE);
    }

    private function requireManagedCustomerScope(subusers_permission_node_key $node, ?int $targetCustomerNumber = null): int
    {
        global $response;

        $auth = new authentication();
        $permission = $this->getOwnPermissionForNode($node);

        $subuser = $auth->get_subuser();
        if ($subuser !== false) {
            $customerNumber = (int)$auth->get_subuser_customer_number_target();
            if ($customerNumber <= 0) {
                $response->error('Unauthorized', 401);
            }
            if ($targetCustomerNumber !== null && $customerNumber !== (int)$targetCustomerNumber) {
                $this->emitForbidden([$permission]);
            }
            self::requirePermission($permission);
            return $customerNumber;
        }

        $user = $auth->get_user();
        if ($user !== false) {
            $customerNumber = (int)$user->customer_number->value();
            if ($customerNumber <= 0) {
                $response->error('Unauthorized', 401);
            }
            if ($targetCustomerNumber !== null && $customerNumber !== (int)$targetCustomerNumber) {
                $this->emitForbidden([$permission]);
            }
            self::requirePermission($permission);
            return $customerNumber;
        }

        $response->error('Unauthorized', 401);
    }

    private function parsePermissionsPayload(mixed $raw, ?array $default = null): ?array
    {
        global $response;

        if ($raw === null) {
            return $default;
        }

        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            if (!is_array($decoded)) {
                $response->error('Invalid permissions payload', 400);
            }
            $raw = $decoded;
        }

        if (!is_array($raw)) {
            $response->error('Invalid permissions type', 400);
        }

        $permissions = [];
        foreach ($raw as $permission) {
            if (!is_string($permission) || subusers_permission_node_key::tryFrom($permission) === null) {
                $response->error('Unknown permission key: ' . (string)$permission, 400);
            }
            $permissions[] = strtoupper(trim($permission));
        }

        return array_values(array_unique($permissions));
    }

    /**
     * @return array{enabled:bool,permissions:array<int,string>}|null
     */
    private function parseAccessTemplatePayload(): ?array
    {
        global $response;

        if (!self::isParametersSet(['permission_template_key'])) {
            return null;
        }

        $templateKey = (string)self::getParameter('permission_template_key');
        try {
            return (new subuser_permission_templates_service())->expandTemplateForWritePayload($templateKey);
        } catch (\InvalidArgumentException $exception) {
            $response->error($exception->getMessage(), 400);
        }

    }

    private function normalizeOptionalString(mixed $value): ?string
    {
        if ($value === null) {
            return null;
        }

        $normalized = trim((string)$value);
        return $normalized === '' ? null : $normalized;
    }

    private function resolveCustomerNames(array $customerNumbers): array
    {
        $customerNumbers = array_values(array_unique(array_filter(
            array_map('intval', $customerNumbers),
            static fn (int $customerNumber): bool => $customerNumber > 0
        )));

        if ($customerNumbers === []) {
            return [];
        }

        return (new users_o())->getCustomerNames($customerNumbers, false);
    }

    private function resolveCustomerName(int $customerNumber, array $customerNames = []): ?string
    {
        if ($customerNumber <= 0) {
            return null;
        }

        $key = (string)$customerNumber;
        $name = $customerNames[$key] ?? null;
        if (!is_string($name)) {
            $name = $this->resolveCustomerNames([$customerNumber])[$key] ?? null;
        }

        $name = trim((string)$name);
        return $name === '' || $name === 'Unknown Customer' ? null : $name;
    }

    private function hasDognvaskAccess(array $permissions, bool $grantEnabled): bool
    {
        if (!$grantEnabled) {
            return false;
        }

        $permissions = subuser_grants_o::normalizePermissionsValue($permissions);
        foreach (self::DOGNVASK_PERMISSION_KEYS as $permission) {
            if (!in_array($permission, $permissions, true)) {
                return false;
            }
        }

        return true;
    }

    private function normalizeAssignedVehicleId(mixed $value): ?int
    {
        global $response;

        if ($value === null || $value === '') {
            return null;
        }

        if (is_int($value)) {
            $id = $value;
        } else {
            $raw = trim((string)$value);
            if ($raw === '') {
                return null;
            }
            if (!preg_match('/^[1-9][0-9]*$/', $raw)) {
                $response->error('Invalid assigned vehicle id', 400);
            }
            $id = (int)$raw;
        }

        if ($id <= 0) {
            $response->error('Invalid assigned vehicle id', 400);
        }

        return $id;
    }

    private function assignedVehiclePayload(?int $vehicleId, ?int $customerNumber = null, bool $strict = false): ?array
    {
        global $response;

        if ($vehicleId === null || $vehicleId <= 0) {
            return null;
        }

        $vehicle = (new customer_vehicles_o())->select($vehicleId);
        if (!$vehicle->exists()) {
            if ($strict) {
                $response->error('Assigned vehicle not found', 404);
            }
            return null;
        }

        $vehicle->getObjectProperties();
        if ($this->customerVehiclesHaveDeletedAtColumn() && $vehicle->deleted_at->value() !== null) {
            if ($strict) {
                $response->error('Assigned vehicle not found', 404);
            }
            return null;
        }

        $vehicleCustomerNumber = (int)$vehicle->customer_id->value();
        if ($customerNumber !== null && $vehicleCustomerNumber !== (int)$customerNumber) {
            if ($strict) {
                $response->error('Assigned vehicle must belong to the selected customer', 400);
            }
            return null;
        }

        $reg = strtoupper(trim((string)$vehicle->reg->value()));
        return [
            'id' => (int)$vehicle->id,
            'reg' => $reg !== '' ? $reg : null,
        ];
    }

    private function customerVehiclesHaveDeletedAtColumn(): bool
    {
        return (new customer_vehicles_o())->columnsExist(['deleted_at']);
    }

    private function validateAssignedVehicleIdForCustomer(mixed $value, int $customerNumber): ?int
    {
        $vehicleId = $this->normalizeAssignedVehicleId($value);
        if ($vehicleId === null) {
            return null;
        }

        $this->assignedVehiclePayload($vehicleId, $customerNumber, true);
        return $vehicleId;
    }

    private function assertSubuserIdentifiersAvailable(
        ?int $phoneCountryCode,
        ?int $phone,
        ?string $username = null,
        ?string $email = null,
        ?int $ignoreSubuserId = null
    ): void {
        global $response;

        if ($phoneCountryCode !== null && $phone !== null) {
            $existingByPhone = (new subusers_o())->getSubuserByPhone($phoneCountryCode, $phone);
            if ($existingByPhone !== null && (int)$existingByPhone->id !== (int)$ignoreSubuserId) {
                $response->error('Account already exists with this phone number', 400);
            }
        }

        if ($username !== null) {
            $existingByUsername = (new subusers_o())->getSubuserByUsername($username);
            if ($existingByUsername !== null && (int)$existingByUsername->id !== (int)$ignoreSubuserId) {
                $response->error('Account already exists with this username', 400);
            }
        }

        if ($email !== null) {
            $existingByEmail = (new subusers_o())->getSubuserByEmail($email);
            if ($existingByEmail !== null && (int)$existingByEmail->id !== (int)$ignoreSubuserId) {
                $response->error('Account already exists with this email address', 400);
            }
        }
    }

    private function resolveSubuserByIdentifiers(
        ?int $phoneCountryCode,
        ?int $phone,
        ?string $username = null,
        ?string $email = null
    ): ?subusers_o {
        global $response;

        $matches = [];

        if ($phoneCountryCode !== null && $phone !== null) {
            $existingByPhone = (new subusers_o())->getSubuserByPhone($phoneCountryCode, $phone);
            if ($existingByPhone !== null) {
                $matches[(int)$existingByPhone->id] = $existingByPhone;
            }
        }

        if ($username !== null) {
            $existingByUsername = (new subusers_o())->getSubuserByUsername($username);
            if ($existingByUsername !== null) {
                $matches[(int)$existingByUsername->id] = $existingByUsername;
            }
        }

        if ($email !== null) {
            $existingByEmail = (new subusers_o())->getSubuserByEmail($email);
            if ($existingByEmail !== null) {
                $matches[(int)$existingByEmail->id] = $existingByEmail;
            }
        }

        if (count($matches) > 1) {
            $response->error('Provided driver identifiers match multiple existing accounts', 409);
        }

        return count($matches) === 1 ? array_values($matches)[0] : null;
    }

    private function frontendBaseUrl(): string
    {
        $frontendBaseUrl = trim((string)(
            getenv('FRONTEND_URL')
            ?: getenv('APP_URL')
            ?: ($_SERVER['FRONTEND_URL'] ?? '')
            ?: ($_SERVER['APP_URL'] ?? '')
            ?: 'https://truckwash.io'
        ));
        return rtrim($frontendBaseUrl !== '' ? $frontendBaseUrl : 'https://truckwash.io', '/');
    }

    private function buildSetupLink(string $token): string
    {
        return $this->frontendBaseUrl() . '/complete-registration?token=' . rawurlencode($token);
    }

    private function buildDirectSubuserLoginPath(string $sessionToken, int $customerNumber): string
    {
        return '/login/qr?token=' . rawurlencode($sessionToken)
            . '&type=subuser&customer_number=' . rawurlencode((string)$customerNumber);
    }

    private function buildDirectSubuserLoginUrl(string $sessionToken, int $customerNumber): string
    {
        return $this->frontendBaseUrl() . $this->buildDirectSubuserLoginPath($sessionToken, $customerNumber);
    }

    private function createDirectSubuserLoginLinkPayload(subusers_o $subuser, string $logAction, string $logMessage): array
    {
        global $response;

        $customerNumber = $this->resolveDirectLoginCustomerNumber($subuser);
        try {
            $sessionToken = $subuser->generateSession();
        } catch (Exception $exception) {
            $response->error($exception->getMessage(), 500);
        }

        $authUser = (new authentication())->get_user();
        (new logs_o())->add(
            'auth',
            'global',
            1,
            $authUser !== false ? (int)$authUser->id : 0,
            $logAction,
            $logMessage . ' for subuser ' . (int)$subuser->id . ' and customer ' . $customerNumber
        );

        return [
            'subuser_id' => (int)$subuser->id,
            'customer_number' => $customerNumber,
            'login_path' => $this->buildDirectSubuserLoginPath($sessionToken, $customerNumber),
            'login_url' => $this->buildDirectSubuserLoginUrl($sessionToken, $customerNumber),
        ];
    }

    private function subuserContactDestination(subusers_o $subuser, string $channel): ?string
    {
        $status = $this->verificationService()->status($subuser);
        $channelStatus = $status[$channel] ?? null;
        if (!is_array($channelStatus) || !($channelStatus['available'] ?? false)) {
            return null;
        }

        $value = trim((string)($channelStatus['value'] ?? ''));
        return $value !== '' ? $value : null;
    }

    private function subuserLinkDelivery(string $channel, string $status, string $message): array
    {
        return [
            'channel' => $channel,
            'status' => $status,
            'message' => $message,
        ];
    }

    private function deliverSubuserDirectLoginLink(subusers_o $subuser, string $channel, string $loginUrl, string $kind): array
    {
        $channel = $this->verificationService()->normalizeChannel($channel);
        $destination = $this->subuserContactDestination($subuser, $channel);
        if ($destination === null) {
            return $this->subuserLinkDelivery($channel, 'missing_destination', 'Der er ingen kontaktoplysning at sende linket til.');
        }

        $isPasswordGuide = $kind === 'password_guide';
        $recipientName = trim((string)($subuser->name->value() ?? ''));
        $recipientName = $recipientName !== '' ? $recipientName : 'Chauffør';

        try {
            if ($channel === subuser_contact_verification_service::CHANNEL_PHONE) {
                $gateway = new gatewayapi();
                if (!$gateway->isEnabled()) {
                    return $this->subuserLinkDelivery($channel, 'unavailable', 'SMS-afsendelse er ikke konfigureret.');
                }
                $message = $isPasswordGuide
                    ? 'Truck Wash: Brug dit personlige link til at logge ind og sætte en ny adgangskode: ' . $loginUrl
                    : 'Truck Wash: Dit personlige loginlink: ' . $loginUrl;
                $gateway->send([$destination], $message);
            } else {
                $subject = $isPasswordGuide
                    ? 'Guide til ny adgangskode hos Truck Wash'
                    : 'Loginlink til Truck Wash';
                $message = $isPasswordGuide
                    ? '<p>Du kan bruge dit personlige link til at logge ind og sætte en ny adgangskode.</p>'
                    : '<p>Du kan bruge dit personlige link til at logge ind på Truck Wash.</p>';
                $message .= '<p><a href="' . htmlspecialchars($loginUrl, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($loginUrl, ENT_QUOTES, 'UTF-8') . '</a></p>';
                (new email())->sendEmail($destination, $recipientName, $subject, $message);
            }
        } catch (Exception) {
            return $this->subuserLinkDelivery($channel, 'failed', 'Linket kunne ikke sendes.');
        }

        return $this->subuserLinkDelivery(
            $channel,
            'sent',
            $isPasswordGuide ? 'Guide til ny adgangskode er sendt.' : 'Loginlink er sendt.'
        );
    }

    private function sendSuperuserSubuserDirectLoginLink(string $kind): void
    {
        global $response;

        $this->requirePermission('edit_subusers');
        $this->requirePermission('SUPERUSER_INTIMIDATE');
        $subuser = $this->loadSubuserOrFail($this->routePositiveInt('subuser_id'));
        try {
            $channel = $this->verificationService()->normalizeChannel((string)$this->fromRoute('channel'));
        } catch (Exception $exception) {
            $response->error($exception->getMessage(), 400);
        }

        $isPasswordGuide = $kind === 'password_guide';
        $linkPayload = $this->createDirectSubuserLoginLinkPayload(
            $subuser,
            $isPasswordGuide
                ? 'SUPERUSER_SUBUSER_PASSWORD_GUIDE_LINK'
                : 'SUPERUSER_SUBUSER_LOGIN_LINK_SENT',
            $isPasswordGuide
                ? 'Created chauffeur password guide link'
                : 'Created chauffeur login link for delivery'
        );
        $delivery = $this->deliverSubuserDirectLoginLink($subuser, $channel, (string)$linkPayload['login_url'], $kind);
        $subuser = $this->loadSubuserOrFail((int)$subuser->id);

        $response->success([
            ...$linkPayload,
            'delivery' => $delivery,
            'subuser' => $this->buildSubuserAccountPayload($subuser),
        ]);
    }

    private function loadSubuserOrFail(int $subuserId): subusers_o
    {
        global $response;

        $subuser = (new subusers_o())->select($subuserId);
        if (!$subuser->exists()) {
            $response->error('Subuser not found', 404);
        }
        $subuser->getObjectProperties();
        return $subuser;
    }

    private function buildSubuserAccountPayload(subusers_o $subuser): array
    {
        return $this->withVerificationPayload($subuser, [
            'id' => (int)$subuser->id,
            'username' => $subuser->username->value(),
            'name' => $subuser->name->value(),
            'email' => $subuser->email->value(),
            'phone_country_code' => $subuser->phone_country_code->value() !== null
                ? (int)$subuser->phone_country_code->value()
                : null,
            'phone' => $subuser->phone->value() !== null ? (int)$subuser->phone->value() : null,
            'setup_required' => $subuser->requiresSetup(),
            'updated_at' => $subuser->updated_at->value() ?? null,
        ]);
    }

    private function verificationService(): subuser_contact_verification_service
    {
        return new subuser_contact_verification_service();
    }

    private function withVerificationPayload(subusers_o $subuser, array $payload): array
    {
        $verification = $this->verificationService()->status($subuser);

        return [
            ...$payload,
            'email_verified' => (bool)($verification['email']['verified'] ?? false),
            'email_verified_at' => $verification['email']['verified_at'] ?? null,
            'phone_verified' => (bool)($verification['phone']['verified'] ?? false),
            'phone_verified_at' => $verification['phone']['verified_at'] ?? null,
            'verification_state' => $verification['state'],
            'verification' => $verification,
        ];
    }

    private function verificationActorContext(string $type, ?int $id = null): array
    {
        return [
            'type' => $type,
            'id' => $id ?? 0,
        ];
    }

    private function sendContactVerificationCode(subusers_o $subuser, string $channel, array $actor): array
    {
        global $response;

        try {
            $delivery = $this->verificationService()->sendCode($subuser, $channel, $actor);
        } catch (Exception $exception) {
            $response->error($exception->getMessage(), 400);
        }

        if (($delivery['status'] ?? null) === 'throttled') {
            $response->error('Please wait before requesting another verification code.', 429);
        }

        return $delivery;
    }

    private function verifyContactVerificationCode(subusers_o $subuser, string $channel, string $code, array $actor): array
    {
        global $response;

        try {
            $result = $this->verificationService()->verifyCode($subuser, $channel, $code, $actor);
        } catch (Exception $exception) {
            $response->error($exception->getMessage(), 400);
        }

        if (($result['status'] ?? null) !== 'verified') {
            $response->error((string)($result['message'] ?? 'Invalid verification code.'), 400);
        }

        return $result;
    }

    private function parseSuperuserSubuserProfileUpdates(int $subuserId): array
    {
        global $response;

        $updates = [];

        if (self::isParametersSet(['name'])) {
            $name = $this->normalizeOptionalString(self::getParameter('name'));
            if ($name === null || strlen($name) < 3 || strlen($name) > 255) {
                $response->error('Name must be between 3 and 255 characters long', 400);
            }
            $updates['name'] = $name;
        }

        if (self::isParametersSet(['username'])) {
            $username = $this->normalizeOptionalString(self::getParameter('username'));
            if ($username !== null && (strlen($username) < 3 || strlen($username) > 50)) {
                $response->error('Username must be between 3 and 50 characters long', 400);
            }
            $updates['username'] = $username;
        }

        if (self::isParametersSet(['email'])) {
            $email = $this->normalizeOptionalString(self::getParameter('email'));
            if ($email !== null && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
                $response->error('Invalid email format', 400);
            }
            if ($email !== null && strlen($email) > 255) {
                $response->error('Email must be at most 255 characters long', 400);
            }
            $updates['email'] = $email;
        }

        $hasPhoneCountryCode = self::isParametersSet(['phone_country_code']);
        $hasPhone = self::isParametersSet(['phone']);
        if ($hasPhoneCountryCode || $hasPhone) {
            if (!$hasPhoneCountryCode || !$hasPhone) {
                $response->error('Phone country code and phone must be provided together', 400);
            }

            $phoneCountryCode = trim((string)self::getParameter('phone_country_code'));
            $phone = trim((string)self::getParameter('phone'));
            if (!preg_match('/^[0-9]{1,3}$/', $phoneCountryCode)) {
                $response->error('Phone country code must be 1-3 digits', 400);
            }
            if (!preg_match('/^[0-9]{4,15}$/', $phone)) {
                $response->error('Phone must be 4-15 digits', 400);
            }

            $updates['phone_country_code'] = (int)$phoneCountryCode;
            $updates['phone'] = (int)$phone;
        }

        if ($updates === []) {
            $response->error('No fields to update', 400);
        }

        $this->assertSubuserIdentifiersAvailable(
            $updates['phone_country_code'] ?? null,
            $updates['phone'] ?? null,
            $updates['username'] ?? null,
            $updates['email'] ?? null,
            $subuserId
        );

        return $updates;
    }

    private function getEnabledGrantForSubuserAndCustomerOrFail(int $subuserId, int $customerNumber): subuser_grants_o
    {
        global $response;

        if ($customerNumber <= 0) {
            $response->error('Customer number is required', 400);
        }

        $grant = (new subuser_grants_o())->getGrantForSubuserAndCustomer($subuserId, $customerNumber, true);
        if ($grant === null || !(bool)$grant->enabled->value() || $grant->deleted_at->value() !== null) {
            $response->error('Enabled subuser grant not found for selected customer', 404);
        }

        return $grant;
    }

    private function resolveDirectLoginCustomerNumber(subusers_o $subuser): int
    {
        global $response;

        if (self::isParametersSet(['grant_id'])) {
            $grant = (new subuser_grants_o())->select((int)self::getParameter('grant_id'));
            if ($grant->exists()) {
                $grant->getObjectProperties();
            }
            if (
                !$grant->exists()
                || (int)$grant->subuser->value() !== (int)$subuser->id
                || !(bool)$grant->enabled->value()
                || $grant->deleted_at->value() !== null
            ) {
                $response->error('Enabled subuser grant not found', 404);
            }

            return (int)$grant->billing_customer_number->value();
        }

        if (self::isParametersSet(['customer_number'])) {
            $customerNumber = (int)self::getParameter('customer_number');
            $this->getEnabledGrantForSubuserAndCustomerOrFail((int)$subuser->id, $customerNumber);
            return $customerNumber;
        }

        $grants = (new subuser_grants_o())->getFieldsWhere([
            'subuser' => (int)$subuser->id,
            'enabled' => 1,
            'deleted_at' => null,
        ], ['billing_customer_number']);

        $customerNumbers = array_values(array_unique(array_map(
            static fn (array $grant): int => (int)($grant['billing_customer_number'] ?? 0),
            $grants
        )));
        $customerNumbers = array_values(array_filter($customerNumbers, static fn (int $customerNumber): bool => $customerNumber > 0));

        if (count($customerNumbers) !== 1) {
            $response->error('Customer number or grant id is required for drivers with multiple customer grants', 400);
        }

        return $customerNumbers[0];
    }

    private function clientThrottleIp(): string
    {
        $remoteAddress = trim((string)($_SERVER['REMOTE_ADDR'] ?? ''));
        return $remoteAddress !== '' ? $remoteAddress : 'unknown';
    }

    private function recordThrottleAttempt(string $scope, string $identifier, int $limit, int $windowSeconds): ?string
    {
        global $response;

        if (!defined('redis')) {
            return null;
        }

        $safeScope = preg_replace('/[^a-z0-9:_-]/i', '_', $scope);
        $key = 'subusers_route_throttle:' . $safeScope . ':' . hash(
            'sha256',
            $this->clientThrottleIp() . ':' . $identifier
        );
        $redis = constant('redis');
        $attempts = (int)($redis->get($key) ?? '0');
        if ($attempts >= $limit) {
            $response->error('Too many attempts. Please wait and try again.', 429);
        }
        $redis->setEx($key, (string)($attempts + 1), $windowSeconds);
        return $key;
    }

    private function clearThrottleAttempt(?string $key): void
    {
        if ($key === null || !defined('redis')) {
            return;
        }
        constant('redis')->delete($key);
    }

    private function subuserAuthFailure(): void
    {
        global $response;
        $response->error('Invalid credentials', 401);
    }

    private function issueSetupInvite(subusers_o $subuser): array
    {
        if (!$subuser->requiresSetup()) {
            return [
                'setup_token' => null,
                'setup_link' => null,
                'delivery' => [
                    'channel' => 'sms',
                    'status' => 'not_required',
                    'message' => 'Driver account already accepted the invitation.',
                ],
            ];
        }

        $token = $subuser->generateSetupToken();
        $link = $this->buildSetupLink($token);
        $delivery = [
            'channel' => 'sms',
            'status' => 'unavailable',
            'message' => 'SMS delivery is not configured.',
        ];

        try {
            $gatewayAPI = new gatewayapi();
            if ($gatewayAPI->isEnabled()) {
                $phoneNumber = (string)$subuser->phone_country_code->value() . (string)$subuser->phone->value();
                $message = 'Tak for din oprettelse af chaufførkonto hos Truck Wash! Klik på linket for at fuldføre registreringen: ' . $link;
                $gatewayAPI->send([$phoneNumber], $message);
                $delivery = [
                    'channel' => 'sms',
                    'status' => 'sent',
                    'message' => 'Invite sent successfully.',
                ];
            }
        } catch (Exception $exception) {
            $delivery = [
                'channel' => 'sms',
                'status' => 'failed',
                'message' => 'Invite delivery failed.',
            ];
        }

        return [
            'setup_token' => $token,
            'setup_link' => $link,
            'delivery' => $delivery,
        ];
    }

    private function buildSubuserManagementPayload(subusers_o $subuser, int $customerNumber, ?string $customerName = null): array
    {
        $grant = (new subuser_grants_o())->getGrantForSubuserAndCustomer((int)$subuser->id, $customerNumber, true);
        $grantPermissions = $grant ? subuser_grants_o::normalizePermissionsValue($grant->permissions->value()) : [];
        $templateService = new subuser_permission_templates_service();
        $setupRequired = $subuser->requiresSetup();
        $grantEnabled = $grant ? (bool)$grant->enabled->value() : false;
        $inviteAccepted = !$setupRequired;
        $assignedVehicleId = $grant && $grant->assigned_vehicle_id->value() !== null
            ? (int)$grant->assigned_vehicle_id->value()
            : null;
        $assignedVehicle = $this->assignedVehiclePayload($assignedVehicleId, $customerNumber);

        $accessState = 'inactive';
        if ($grant !== null && $grantEnabled) {
            $accessState = $setupRequired ? 'pending_setup' : 'active';
        } elseif ($grant !== null) {
            $accessState = 'disabled';
        }

        return $this->withVerificationPayload($subuser, [
            'id' => (int)$subuser->id,
            'username' => $subuser->username->value(),
            'name' => $subuser->name->value(),
            'email' => $subuser->email->value(),
            'phone_country_code' => $subuser->phone_country_code->value() !== null ? (int)$subuser->phone_country_code->value() : null,
            'phone' => $subuser->phone->value() !== null ? (int)$subuser->phone->value() : null,
            'created_at' => $subuser->created_at->value() ?? null,
            'updated_at' => $subuser->updated_at->value() ?? null,
            'suspended_at' => $subuser->suspended_at->value() ?? null,
            'two_factor_enabled' => $subuser->isTwoFactorEnabled(),
            'setup_required' => $setupRequired,
            'invite_accepted' => $inviteAccepted,
            'can_resend_invite' => $setupRequired,
            'profile_editable_by_manager' => false,
            'customer_number' => $customerNumber,
            'customer_name' => $customerName ?? $this->resolveCustomerName($customerNumber),
            'grant_id' => $grant ? (int)$grant->id : null,
            'grant_enabled' => $grantEnabled,
            'grant_note' => $grant ? $grant->note->value() : null,
            'assigned_vehicle_id' => $assignedVehicle['id'] ?? null,
            'assigned_vehicle_reg' => $assignedVehicle['reg'] ?? null,
            'assigned_vehicle' => $assignedVehicle,
            'dognvask_enabled' => $this->hasDognvaskAccess($grantPermissions, $grantEnabled),
            'grant_permissions' => $grantPermissions,
            'permissions' => $grantPermissions,
            'permission_template_key' => $templateService->classify($grantPermissions, $grantEnabled),
            'permission_groups' => $templateService->permissionGroups($grantPermissions),
            'access_state' => $accessState,
        ]);
    }

    private function buildCurrentSubuserPayload(subusers_o $subuser): array
    {
        $grants = (new subuser_grants_o())->getFieldsWhere([
            'subuser' => $subuser->id,
            'enabled' => 1,
            'deleted_at' => null,
        ], ['id', 'permissions', 'billing_customer_number']);
        $customerNames = $this->resolveCustomerNames(array_map(
            static fn (array $grant): int => (int)($grant['billing_customer_number'] ?? 0),
            $grants
        ));

        return $this->withVerificationPayload($subuser, [
            'id' => (int)$subuser->id,
            'username' => $subuser->username->value(),
            'name' => $subuser->name->value(),
            'email' => $subuser->email->value(),
            'phone_country_code' => $subuser->phone_country_code->value() !== null ? (int)$subuser->phone_country_code->value() : null,
            'phone' => $subuser->phone->value() !== null ? (int)$subuser->phone->value() : null,
            'grants' => array_map(function ($grant) use ($customerNames) {
                $customerNumber = (int)$grant['billing_customer_number'];
                return [
                    'grant_id' => isset($grant['id']) ? (int)$grant['id'] : null,
                    'name' => $this->resolveCustomerName($customerNumber, $customerNames),
                    'billing_customer_number' => $customerNumber,
                    'permissions' => subuser_grants_o::normalizePermissionsValue($grant['permissions'] ?? null),
                ];
            }, $grants),
            'created_at' => $subuser->created_at->value() ?? null,
            'updated_at' => $subuser->updated_at->value() ?? null,
            'suspended_at' => $subuser->suspended_at->value() ?? null,
            'two_factor_enabled' => $subuser->isTwoFactorEnabled(),
        ]);
    }

    private function parseSuperuserPaginationRequest(): array
    {
        global $response;

        $page = max(1, (int)($response->getRequestParameter('page') ?: 1));
        $limitRaw = $response->getRequestParameter('limit');
        $limit = is_string($limitRaw) && strtolower($limitRaw) === 'all'
            ? 1000
            : (int)($limitRaw ?: 100);
        $limit = max(1, min($limit, 1000));

        $search = $this->normalizeOptionalString($response->getRequestParameter('search'));
        $orderRaw = (string)($response->getRequestParameter('order') ?: 'created_at:DESC');
        $orderParts = explode(':', $orderRaw, 2);
        $orderField = $orderParts[0] ?? 'created_at';
        $orderDirection = strtoupper($orderParts[1] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
        $allowedOrderFields = [
            'id' => 's.`id`',
            'created_at' => 's.`created_at`',
            'updated_at' => 'row_updated_at',
            'customer_number' => 'sort_customer_number',
            'grant_id' => 'sort_grant_id',
            'name' => 's.`name`',
        ];

        if (!isset($allowedOrderFields[$orderField])) {
            $orderField = 'created_at';
        }

        return [
            'page' => $page,
            'limit' => $limit,
            'search' => $search,
            'order_field' => $orderField,
            'order_sql' => $allowedOrderFields[$orderField],
            'order_direction' => $orderDirection,
        ];
    }

    private function bindStatementParameters(\mysqli_stmt $statement, string $types, array $params): void
    {
        if ($params === []) {
            return;
        }

        $refs = [];
        foreach ($params as $key => $value) {
            $refs[$key] = &$params[$key];
        }

        $statement->bind_param($types, ...$refs);
    }

    private function buildSuperuserGrantPayload(array $row, bool $setupRequired): array
    {
        $grantPermissions = subuser_grants_o::normalizePermissionsValue($row['grant_permissions'] ?? null);
        $templateService = new subuser_permission_templates_service();
        $grantEnabled = (bool)((int)($row['grant_enabled'] ?? 0));
        $assignedVehicleId = isset($row['assigned_vehicle_id']) && $row['assigned_vehicle_id'] !== null
            ? (int)$row['assigned_vehicle_id']
            : null;
        $assignedVehicleReg = isset($row['assigned_vehicle_reg']) && trim((string)$row['assigned_vehicle_reg']) !== ''
            ? strtoupper(trim((string)$row['assigned_vehicle_reg']))
            : null;
        $assignedVehicle = $assignedVehicleId !== null
            ? ['id' => $assignedVehicleId, 'reg' => $assignedVehicleReg]
            : null;

        $accessState = 'inactive';
        if (!empty($row['grant_id']) && $grantEnabled) {
            $accessState = $setupRequired ? 'pending_setup' : 'active';
        } elseif (!empty($row['grant_id'])) {
            $accessState = 'disabled';
        }

        return [
            'grant_id' => (int)$row['grant_id'],
            'customer_number' => (int)$row['customer_number'],
            'customer_name' => $row['customer_name'] ?: null,
            'grant_enabled' => $grantEnabled,
            'grant_note' => $row['grant_note'] ?? null,
            'assigned_vehicle_id' => $assignedVehicleId,
            'assigned_vehicle_reg' => $assignedVehicleReg,
            'assigned_vehicle' => $assignedVehicle,
            'dognvask_enabled' => $this->hasDognvaskAccess($grantPermissions, $grantEnabled),
            'grant_permissions' => $grantPermissions,
            'permissions' => $grantPermissions,
            'permission_template_key' => $templateService->classify($grantPermissions, $grantEnabled),
            'permission_groups' => $templateService->permissionGroups($grantPermissions),
            'grant_created_at' => $row['grant_created_at'] ?? null,
            'grant_updated_at' => $row['grant_updated_at'] ?? null,
            'access_state' => $accessState,
        ];
    }

    private function buildSuperuserSubuserManagementPayload(array $row, array $grantRows = []): array
    {
        $setupRequired = !is_string($row['password'] ?? null) || trim((string)$row['password']) === '';
        if ($grantRows === [] && !empty($row['grant_id'])) {
            $grantRows = [$row];
        }

        $grants = array_map(
            fn (array $grantRow): array => $this->buildSuperuserGrantPayload($grantRow, $setupRequired),
            $this->dedupeSuperuserGrantRows($grantRows)
        );
        $primaryGrant = $this->selectPrimarySuperuserGrant($grants);

        $accessState = 'inactive';
        if (array_filter($grants, static fn (array $grant): bool => $grant['access_state'] === 'active') !== []) {
            $accessState = 'active';
        } elseif (array_filter($grants, static fn (array $grant): bool => $grant['access_state'] === 'pending_setup') !== []) {
            $accessState = 'pending_setup';
        } elseif ($grants !== []) {
            $accessState = 'disabled';
        }

        return $this->withVerificationPayload($this->subuserFromSuperuserRow($row), [
            'id' => (int)$row['id'],
            'username' => $row['username'] ?? null,
            'name' => $row['name'] ?? null,
            'email' => $row['email'] ?? null,
            'phone_country_code' => $row['phone_country_code'] !== null ? (int)$row['phone_country_code'] : null,
            'phone' => $row['phone'] !== null ? (int)$row['phone'] : null,
            'created_at' => $row['created_at'] ?? null,
            'updated_at' => $row['row_updated_at'] ?? $row['updated_at'] ?? null,
            'suspended_at' => $row['suspended_at'] ?? null,
            'two_factor_enabled' => (bool)((int)($row['two_factor_enabled'] ?? 0)),
            'setup_required' => $setupRequired,
            'invite_accepted' => !$setupRequired,
            'can_resend_invite' => $setupRequired,
            'profile_editable_by_manager' => false,
            'customer_number' => $primaryGrant['customer_number'] ?? null,
            'customer_name' => $primaryGrant['customer_name'] ?? null,
            'grant_id' => $primaryGrant['grant_id'] ?? null,
            'grant_enabled' => $primaryGrant['grant_enabled'] ?? false,
            'grant_note' => $primaryGrant['grant_note'] ?? null,
            'assigned_vehicle_id' => $primaryGrant['assigned_vehicle_id'] ?? null,
            'assigned_vehicle_reg' => $primaryGrant['assigned_vehicle_reg'] ?? null,
            'assigned_vehicle' => $primaryGrant['assigned_vehicle'] ?? null,
            'dognvask_enabled' => $primaryGrant['dognvask_enabled'] ?? false,
            'grant_permissions' => $primaryGrant['grant_permissions'] ?? [],
            'permissions' => $primaryGrant['permissions'] ?? [],
            'permission_template_key' => $primaryGrant['permission_template_key'] ?? subuser_permission_templates_service::TEMPLATE_DEACTIVATED,
            'permission_groups' => $primaryGrant['permission_groups'] ?? [],
            'grant_created_at' => $primaryGrant['grant_created_at'] ?? null,
            'grant_updated_at' => $primaryGrant['grant_updated_at'] ?? null,
            'grants' => $grants,
            'grant_count' => count($grants),
            'customer_numbers' => array_values(array_unique(array_map(
                static fn (array $grant): int => (int)$grant['customer_number'],
                $grants
            ))),
            'access_state' => $accessState,
        ]);
    }

    private function subuserFromSuperuserRow(array $row): subusers_o
    {
        $subuser = (new subusers_o())->select((int)$row['id']);
        $subuser->getObjectProperties();
        return $subuser;
    }

    private function dedupeSuperuserGrantRows(array $grantRows): array
    {
        $byCustomer = [];
        foreach ($grantRows as $grantRow) {
            $customerNumber = (int)($grantRow['customer_number'] ?? 0);
            if ($customerNumber <= 0) {
                continue;
            }

            $existing = $byCustomer[$customerNumber] ?? null;
            if ($existing === null || $this->compareSuperuserGrantRows($grantRow, $existing) < 0) {
                $byCustomer[$customerNumber] = $grantRow;
            }
        }

        $deduped = array_values($byCustomer);
        usort($deduped, fn (array $left, array $right): int => $this->compareSuperuserGrantRows($left, $right));

        return $deduped;
    }

    private function compareSuperuserGrantRows(array $left, array $right): int
    {
        $leftEnabled = (int)($left['grant_enabled'] ?? 0);
        $rightEnabled = (int)($right['grant_enabled'] ?? 0);
        if ($leftEnabled !== $rightEnabled) {
            return $rightEnabled <=> $leftEnabled;
        }

        $leftCustomer = (int)($left['customer_number'] ?? 0);
        $rightCustomer = (int)($right['customer_number'] ?? 0);
        if ($leftCustomer !== $rightCustomer) {
            return $leftCustomer <=> $rightCustomer;
        }

        $leftUpdated = strtotime((string)($left['grant_updated_at'] ?? $left['grant_created_at'] ?? '')) ?: 0;
        $rightUpdated = strtotime((string)($right['grant_updated_at'] ?? $right['grant_created_at'] ?? '')) ?: 0;
        if ($leftUpdated !== $rightUpdated) {
            return $rightUpdated <=> $leftUpdated;
        }

        return (int)($right['grant_id'] ?? 0) <=> (int)($left['grant_id'] ?? 0);
    }

    private function selectPrimarySuperuserGrant(array $grants): ?array
    {
        if ($grants === []) {
            return null;
        }

        $sorted = $grants;
        usort($sorted, static function (array $left, array $right): int {
            $leftUpdated = strtotime((string)($left['grant_updated_at'] ?? $left['grant_created_at'] ?? '')) ?: 0;
            $rightUpdated = strtotime((string)($right['grant_updated_at'] ?? $right['grant_created_at'] ?? '')) ?: 0;
            if ($leftUpdated !== $rightUpdated) {
                return $rightUpdated <=> $leftUpdated;
            }

            $leftCreated = strtotime((string)($left['grant_created_at'] ?? '')) ?: 0;
            $rightCreated = strtotime((string)($right['grant_created_at'] ?? '')) ?: 0;
            if ($leftCreated !== $rightCreated) {
                return $rightCreated <=> $leftCreated;
            }

            return (int)($right['grant_id'] ?? 0) <=> (int)($left['grant_id'] ?? 0);
        });

        return $sorted[0];
    }

    private function routePositiveInt(string $name): int
    {
        global $response;

        $raw = $this->fromRoute($name);
        if (!is_string($raw) || !preg_match('/^[1-9][0-9]*$/', $raw)) {
            $response->error('Invalid route parameter', 400);
        }

        return (int)$raw;
    }

    private function resolveSuperuserSubuserTargetUser(int $userId): array
    {
        global $response;

        $targetUser = (new users_o())->select($userId);
        if (!$targetUser->exists()) {
            $response->error('User not found', 404);
        }
        $targetUser->getObjectProperties();

        $customerNumber = (int)$targetUser->customer_number->value();
        if ($customerNumber <= 0) {
            $response->error('Selected user does not have a customer number', 400);
        }

        $customerName = $targetUser->display_name->value();
        if (!is_string($customerName) || trim($customerName) === '') {
            $customerName = $this->resolveCustomerName($customerNumber);
        }

        return [
            'user_id' => (int)$targetUser->id,
            'customer_number' => $customerNumber,
            'customer_name' => $customerName,
        ];
    }

    private function buildSubuserSummaryForCustomer(int $customerNumber): array
    {
        global $db;

        $statement = $db->conn->prepare("
            SELECT
                COUNT(*) AS `total`,
                SUM(CASE WHEN g.`enabled` = 1 AND COALESCE(s.`password`, '') <> '' THEN 1 ELSE 0 END) AS `active`,
                SUM(CASE WHEN g.`enabled` = 1 AND COALESCE(s.`password`, '') = '' THEN 1 ELSE 0 END) AS `pending_setup`,
                SUM(CASE WHEN g.`enabled` = 0 THEN 1 ELSE 0 END) AS `disabled`
            FROM `subuser_grants` g
            INNER JOIN `subusers` s ON s.`id` = g.`subuser`
            WHERE g.`deleted_at` IS NULL
              AND g.`billing_customer_number` = ?
        ");
        if ($statement === false) {
            throw new Exception('Failed to prepare subuser summary query: ' . $db->conn->error);
        }

        $statement->bind_param('i', $customerNumber);
        $statement->execute();
        $result = $statement->get_result();
        $row = $result->fetch_assoc() ?: [];
        $statement->close();

        return [
            'total' => (int)($row['total'] ?? 0),
            'active' => (int)($row['active'] ?? 0),
            'pending_setup' => (int)($row['pending_setup'] ?? 0),
            'disabled' => (int)($row['disabled'] ?? 0),
        ];
    }

    private function addUserScopedSubuserMeta(array $targetUser): void
    {
        global $response;

        $response->add_meta('user_context', $targetUser);
        $response->add_meta('subusers_summary', $this->buildSubuserSummaryForCustomer((int)$targetUser['customer_number']));
    }

    private function listSuperuserSubusers(?int $customerNumber = null): array
    {
        global $db, $response;

        $pagination = $this->parseSuperuserPaginationRequest();
        $offset = ((int)$pagination['page'] - 1) * (int)$pagination['limit'];
        $where = ['g.`deleted_at` IS NULL'];
        $params = [];
        $types = '';

        $includeNonEnabled = true;
        if (self::isParametersSet(['include_non_enabled'])) {
            $tmp = filter_var(self::getParameter('include_non_enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
            $includeNonEnabled = $tmp === null ? true : (bool)$tmp;
        }
        if (!$includeNonEnabled) {
            $where[] = 'g.`enabled` = 1';
        }
        if ($customerNumber !== null) {
            $where[] = 'g.`billing_customer_number` = ?';
            $params[] = $customerNumber;
            $types .= 'i';
        }

        if ($pagination['search'] !== null) {
            $where[] = "(
                CAST(s.`id` AS CHAR) LIKE ?
                OR s.`username` LIKE ?
                OR s.`name` LIKE ?
                OR s.`email` LIKE ?
                OR CAST(s.`phone_country_code` AS CHAR) LIKE ?
                OR CAST(s.`phone` AS CHAR) LIKE ?
                OR cv.`reg` LIKE ?
                OR CAST(g.`billing_customer_number` AS CHAR) LIKE ?
                OR g.`note` LIKE ?
                OR EXISTS (
                    SELECT 1
                    FROM `users` search_u
                    WHERE search_u.`customer_number` = g.`billing_customer_number`
                      AND search_u.`display_name` LIKE ?
                )
            )";
            $search = '%' . $pagination['search'] . '%';
            for ($i = 0; $i < 10; $i++) {
                $params[] = $search;
                $types .= 's';
            }
        }

        $whereSql = 'WHERE ' . implode(' AND ', $where);
        $vehicleDeletedAtJoinSql = $this->customerVehiclesHaveDeletedAtColumn()
            ? ' AND cv.`deleted_at` IS NULL'
            : '';
        $fromSql = "
            FROM `subuser_grants` g
            INNER JOIN `subusers` s ON s.`id` = g.`subuser`
            LEFT JOIN `customer_vehicles` cv ON cv.`id` = g.`assigned_vehicle_id`{$vehicleDeletedAtJoinSql}
        ";

        $countSql = "SELECT COUNT(DISTINCT s.`id`) AS `count` $fromSql $whereSql";
        $countStatement = $db->conn->prepare($countSql);
        if ($countStatement === false) {
            throw new Exception('Failed to prepare subuser count query: ' . $db->conn->error);
        }
        $this->bindStatementParameters($countStatement, $types, $params);
        $countStatement->execute();
        $countResult = $countStatement->get_result();
        $total = (int)($countResult->fetch_assoc()['count'] ?? 0);
        $countStatement->close();

        $pageSql = "
            SELECT
                s.`id`,
                s.`username`,
                s.`password`,
                s.`name`,
                s.`email`,
                s.`phone_country_code`,
                s.`phone`,
                s.`two_factor_enabled`,
                s.`created_at`,
                s.`updated_at`,
                s.`suspended_at`,
                MAX(COALESCE(g.`updated_at`, s.`updated_at`)) AS `row_updated_at`,
                MIN(g.`billing_customer_number`) AS `sort_customer_number`,
                MAX(g.`id`) AS `sort_grant_id`
            $fromSql
            $whereSql
            GROUP BY
                s.`id`,
                s.`username`,
                s.`password`,
                s.`name`,
                s.`email`,
                s.`phone_country_code`,
                s.`phone`,
                s.`two_factor_enabled`,
                s.`created_at`,
                s.`updated_at`,
                s.`suspended_at`
            ORDER BY {$pagination['order_sql']} {$pagination['order_direction']}
            LIMIT ? OFFSET ?
        ";

        $pageStatement = $db->conn->prepare($pageSql);
        if ($pageStatement === false) {
            throw new Exception('Failed to prepare subuser list query: ' . $db->conn->error);
        }
        $pageParams = [...$params, (int)$pagination['limit'], $offset];
        $this->bindStatementParameters($pageStatement, $types . 'ii', $pageParams);
        $pageStatement->execute();
        $result = $pageStatement->get_result();
        $rows = $result->fetch_all(MYSQLI_ASSOC);
        $pageStatement->close();

        $subuserIds = array_values(array_map(
            static fn (array $row): int => (int)($row['id'] ?? 0),
            $rows
        ));
        if ($subuserIds === []) {
            $response->paginate(
                (int)$pagination['page'],
                (int)$pagination['limit'],
                $total,
                $pagination['search'],
                null,
                [$pagination['order_field'] => $pagination['order_direction']]
            );

            return [];
        }

        $grantWhere = [
            'g.`deleted_at` IS NULL',
            'g.`subuser` IN (' . implode(', ', array_fill(0, count($subuserIds), '?')) . ')',
        ];
        $grantParams = $subuserIds;
        $grantTypes = str_repeat('i', count($subuserIds));
        if (!$includeNonEnabled) {
            $grantWhere[] = 'g.`enabled` = 1';
        }
        if ($customerNumber !== null) {
            $grantWhere[] = 'g.`billing_customer_number` = ?';
            $grantParams[] = $customerNumber;
            $grantTypes .= 'i';
        }

        $grantSql = "
            SELECT
                g.`subuser` AS `subuser_id`,
                g.`id` AS `grant_id`,
                g.`billing_customer_number` AS `customer_number`,
                g.`enabled` AS `grant_enabled`,
                g.`note` AS `grant_note`,
                g.`permissions` AS `grant_permissions`,
                g.`assigned_vehicle_id`,
                cv.`reg` AS `assigned_vehicle_reg`,
                g.`created_at` AS `grant_created_at`,
                g.`updated_at` AS `grant_updated_at`
            FROM `subuser_grants` g
            LEFT JOIN `customer_vehicles` cv ON cv.`id` = g.`assigned_vehicle_id`{$vehicleDeletedAtJoinSql}
            WHERE " . implode(' AND ', $grantWhere) . "
            ORDER BY g.`enabled` DESC, g.`billing_customer_number` ASC, g.`id` DESC
        ";

        $grantStatement = $db->conn->prepare($grantSql);
        if ($grantStatement === false) {
            throw new Exception('Failed to prepare subuser grant list query: ' . $db->conn->error);
        }
        $this->bindStatementParameters($grantStatement, $grantTypes, $grantParams);
        $grantStatement->execute();
        $grantResult = $grantStatement->get_result();
        $grantRows = $grantResult->fetch_all(MYSQLI_ASSOC);
        $grantStatement->close();

        $customerNames = $this->resolveCustomerNames(array_map(
            static fn (array $row): int => (int)($row['customer_number'] ?? 0),
            $grantRows
        ));
        $grantRows = array_map(function (array $row) use ($customerNames): array {
            $customerNumberForGrant = (int)($row['customer_number'] ?? 0);
            $row['customer_name'] = $this->resolveCustomerName($customerNumberForGrant, $customerNames);
            return $row;
        }, $grantRows);
        $grantRowsBySubuser = [];
        foreach ($grantRows as $grantRow) {
            $subuserId = (int)($grantRow['subuser_id'] ?? 0);
            if ($subuserId <= 0) {
                continue;
            }

            $grantRowsBySubuser[$subuserId] ??= [];
            $grantRowsBySubuser[$subuserId][] = $grantRow;
        }

        $response->paginate(
            (int)$pagination['page'],
            (int)$pagination['limit'],
            $total,
            $pagination['search'],
            null,
            [$pagination['order_field'] => $pagination['order_direction']]
        );

        return array_map(
            fn(array $row): array => $this->buildSuperuserSubuserManagementPayload(
                $row,
                $grantRowsBySubuser[(int)($row['id'] ?? 0)] ?? []
            ),
            $rows
        );
    }

    private function getGrantForScopedUserOrFail(int $grantId, int $customerNumber): subuser_grants_o
    {
        global $response;

        $grant = (new subuser_grants_o())->select($grantId);
        if (!$grant->exists()) {
            $response->error('Subuser grant not found', 404);
        }
        $grant->getObjectProperties();

        if ((int)$grant->billing_customer_number->value() !== $customerNumber || $grant->deleted_at->value() !== null) {
            $response->error('Subuser grant not found for selected user', 404);
        }

        return $grant;
    }

    private function patchScopedSubuserGrant(int $grantId, int $customerNumber): void
    {
        global $response;

        $grant = $this->getGrantForScopedUserOrFail($grantId, $customerNumber);
        $updates = [];
        $templateAccess = $this->parseAccessTemplatePayload();
        if ($templateAccess !== null) {
            $updates['enabled'] = $templateAccess['enabled'];
            $updates['permissions'] = $templateAccess['permissions'];
        }

        if (self::isParametersSet(['enabled'])) {
            $tmp = filter_var(self::getParameter('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
            if ($tmp === null) {
                $response->error('Invalid enabled value', 400);
            }
            $updates['enabled'] = (bool)$tmp;
        }

        if (self::isParametersSet(['note'])) {
            $note = $this->normalizeOptionalString(self::getParameter('note'));
            if ($note !== null && strlen($note) > 65535) {
                $response->error('Note must be at most 65535 characters long', 400);
            }
            $updates['note'] = $note;
        }

        if (self::isParametersSet(['permissions'])) {
            $updates['permissions'] = $this->parsePermissionsPayload(self::getParameter('permissions'), []);
        }

        if (self::isParametersSet(['assigned_vehicle_id'])) {
            $updates['assigned_vehicle_id'] = $this->validateAssignedVehicleIdForCustomer(
                self::getParameter('assigned_vehicle_id'),
                $customerNumber
            );
        }

        if ($updates === []) {
            $response->error('No fields to update', 400);
        }

        try {
            $grant->update($updates);
        } catch (Exception $exception) {
            $response->error('Failed to update subuser grant', 500);
        }

        $updatedGrant = (new subuser_grants_o())->select($grantId);
        $updatedGrant->getObjectProperties();
        $subuser = (new subusers_o())->select((int)$updatedGrant->subuser->value());
        $subuser->getObjectProperties();

        $response->success([
            'subuser' => $this->buildSubuserManagementPayload($subuser, $customerNumber),
            'grant' => $updatedGrant->asArray(),
        ]);
    }

    private function resendInviteForScopedUser(int $subuserId, int $customerNumber): void
    {
        global $response;

        $subuser = (new subusers_o())->select($subuserId);
        if (!$subuser->exists()) {
            $response->error('Subuser not found', 404);
        }
        $subuser->getObjectProperties();

        $grant = (new subuser_grants_o())->getGrantForSubuserAndCustomer($subuserId, $customerNumber, true);
        if ($grant === null) {
            $response->error('Subuser grant not found for selected user', 404);
        }

        if (!$subuser->requiresSetup()) {
            $response->error('Driver account already accepted the invitation.', 409);
        }

        $invite = $this->issueSetupInvite($subuser);
        $response->success([
            'subuser' => $this->buildSubuserManagementPayload($subuser, $customerNumber),
            'grant' => $grant->asArray(),
            'invite' => $invite,
        ]);
    }

    private function handleInviteSubuserForCustomer(int $customerNumber): void
    {
        global $response;

        if ($customerNumber <= 0) {
            $response->error('Customer number is required', 400);
        }
        if (self::isParametersSet(['customer_number']) && (int)self::getParameter('customer_number') !== $customerNumber) {
            $response->error('Customer number does not match selected user', 400);
        }

        self::requireParameters(['name', 'phone_country_code', 'phone']);

        $name = $this->normalizeOptionalString(self::getParameter('name'));
        $phoneCountryCode = (int)self::getParameter('phone_country_code');
        $phone = (int)self::getParameter('phone');
        $note = self::isParametersSet(['note']) ? $this->normalizeOptionalString(self::getParameter('note')) : null;
        $templateAccess = $this->parseAccessTemplatePayload();
        $enabled = true;
        if (self::isParametersSet(['enabled'])) {
            $tmp = filter_var(self::getParameter('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
            $enabled = $tmp === null ? true : (bool)$tmp;
        }
        $permissions = self::isParametersSet(['permissions'])
            ? $this->parsePermissionsPayload(self::getParameter('permissions'), [])
            : null;
        if ($templateAccess !== null) {
            $enabled = $templateAccess['enabled'];
            $permissions = $templateAccess['permissions'];
        }

        if ($name === null || strlen($name) < 3 || strlen($name) > 255) {
            $response->error('Name must be between 3 and 255 characters long', 400);
        }
        self::requireType($phoneCountryCode, self::type_int());
        self::requireType($phone, self::type_int());
        self::requireMinLength('phone_country_code', 1);
        self::requireMaxLength('phone_country_code', 3);
        self::requireMinLength('phone', 4);
        self::requireMaxLength('phone', 15);
        if ($note !== null && strlen($note) > 65535) {
            $response->error('Note must be at most 65535 characters long', 400);
        }

        $subuser = (new subusers_o())->getSubuserByPhone($phoneCountryCode, $phone);
        if ($subuser === null) {
            try {
                $subuser = (new subusers_o())->add(
                    null,
                    null,
                    $name,
                    null,
                    $phoneCountryCode,
                    $phone
                );
            } catch (Exception $exception) {
                $response->error($exception->getMessage(), 400);
            }
        }

        $grant = (new subuser_grants_o())->getGrantForSubuserAndCustomer((int)$subuser->id, $customerNumber, true);
        if ($grant === null) {
            try {
                $grant = (new subuser_grants_o())->add(
                    $customerNumber,
                    (int)$subuser->id,
                    $enabled,
                    $note,
                    $permissions ?? subuser_grants_o::defaultPermissions
                );
            } catch (Exception $exception) {
                $response->error('Failed to create subuser grant', 500);
            }
        } else {
            $grantUpdates = ['enabled' => $enabled];
            if (self::isParametersSet(['note'])) {
                $grantUpdates['note'] = $note;
            }
            if ($permissions !== null) {
                $grantUpdates['permissions'] = $permissions;
            }
            try {
                $grant->update($grantUpdates);
            } catch (Exception $exception) {
                $response->error('Failed to update subuser grant', 500);
            }
            $grant = (new subuser_grants_o())->select((int)$grant->id);
            $grant->getObjectProperties();
        }

        $invite = $this->issueSetupInvite($subuser);
        $response->success([
            'subuser' => $this->buildSubuserManagementPayload($subuser, $customerNumber),
            'grant' => $grant->asArray(),
            'invite' => $invite,
        ]);
    }

    public function run(): void
    {
        subusers_schema_bootstrap::ensureTables();

        // =============================
        // Subuser Grant Management
        // =============================
        $this->get('/subusers/grants', function () {
            global $response;
            /** Permissions (subuser-aware) */
            $permission_own = self::definePermission('list_own_subuser_grants', subusers_permission_node_key::SUBUSERS_LIST);
            $permission_other = self::definePermission('list_subuser_grants');
            $has_permission_other = self::hasPermission($permission_other);

            // Optional filters: customer_number, subuser_id
            $filters = ['deleted_at' => null];
            $hasFilter = false;
            $requestedCustomer = null;
            if (self::isParametersSet(['customer_number'])) {
                $customer_number = (int)self::getParameter('customer_number');
                self::requireType($customer_number, self::type_int());
                $filters['billing_customer_number'] = $customer_number;
                $requestedCustomer = (int)$customer_number;
                $hasFilter = true;
            }
            if (self::isParametersSet(['subuser_id'])) {
                $subuser_id = (int)self::getParameter('subuser_id');
                self::requireType($subuser_id, self::type_int());
                $filters['subuser'] = $subuser_id;
                $hasFilter = true;
            }

            // Effective customer context (classic or subuser header)
            $effectiveCustomer = self::resolveEffectiveCustomerNumber();

            // Allow via own-scope or admin-scope
            self::allowOwnOrDepartmentAccess(
                $permission_own,
                $permission_other,
                $requestedCustomer !== null ? (int)$requestedCustomer : ($effectiveCustomer !== null ? (int)$effectiveCustomer : null),
                null,
                null,
                'You do not have permission to list subuser grants.'
            );

            // If not admin/department permission, force restrict to effective customer
            if (!$has_permission_other) {
                if ($effectiveCustomer === null) {
                    $response->forbidden([$permission_other->permission]);
                }
                $filters['billing_customer_number'] = (int)$effectiveCustomer;
                $hasFilter = true; // ensure we don't fail below
            }

            // Fallback: try to infer from session for classic users
            if (!$hasFilter) {
                $user = (new authentication())->get_user();
                if ($user && !empty($user->customer_number->value())) {
                    $filters['billing_customer_number'] = (int)$user->customer_number->value();
                    $hasFilter = true;
                }
            }

            if (!$hasFilter) {
                $response->error('You must provide at least one of: customer_number or subuser_id', 400);
            }

            $paginatedResponse = (new subuser_grants_o())->listObjectsWithPaginationIfSet(function ($o) {
                $o = (object)$o;
                $subuser = (new subusers_o())->select((int)$o->subuser);
                return [
                    'id' => $o->id,
                    'billing_customer_number' => $o->billing_customer_number,
                    'subuser' => $o->subuser,
                    'name' => $subuser->name->value(),
                    'enabled' => $o->enabled,
                    'note' => $o->note,
                    'assigned_vehicle_id' => isset($o->assigned_vehicle_id) && $o->assigned_vehicle_id !== null ? (int)$o->assigned_vehicle_id : null,
                    'permissions' => subuser_grants_o::normalizePermissionsValue($o->permissions ?? null),
                    'created_at' => $o->created_at,
                    'updated_at' => $o->updated_at,
                ];
            }, $filters);
            $response->success($paginatedResponse);
        }, [
            'list_subuser_grants' => 'List subuser grants for any customer (admin).',
            'list_own_subuser_grants' => 'List subuser grants for the effective customer. Subusers require node: SUBUSERS_LIST and X-Customer-Number header.'
        ]);

        $this->post('/subusers/grants', function () {
            global $response;
            /** Permissions (subuser-aware) */
            $permission_own = self::definePermission('add_own_subusers', subusers_permission_node_key::SUBUSERS_ADD);
            $permission_other = self::definePermission('manage_subuser_grants');

            self::requireParameters(['customer_number', 'subuser_id']);
            $customer_number = (int)self::getParameter('customer_number');
            $subuser_id = (int)self::getParameter('subuser_id');
            self::requireType($customer_number, self::type_int());
            self::requireType($subuser_id, self::type_int());

            if (!self::hasPermission($permission_other, (int)$customer_number)) {
                $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_ADD, (int)$customer_number);
            }

            $enabled = true;
            if (self::isParametersSet(['enabled'])) {
                $enabled = (bool)filter_var(self::getParameter('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
                if ($enabled === null) { $enabled = true; }
            }
            $note = null;
            if (self::isParametersSet(['note'])) {
                $note = (string)self::getParameter('note');
                self::requireType($note, self::type_string());
                self::requireMaxLength('note', 65535);
            }
            $templateAccess = $this->parseAccessTemplatePayload();
            $permissions = self::isParametersSet(['permissions'])
                ? $this->parsePermissionsPayload(self::getParameter('permissions'), [])
                : null;
            if ($templateAccess !== null) {
                $enabled = $templateAccess['enabled'];
                $permissions = $templateAccess['permissions'];
            }
            $assignedVehicleId = null;
            if (self::isParametersSet(['assigned_vehicle_id'])) {
                $assignedVehicleId = $this->validateAssignedVehicleIdForCustomer(
                    self::getParameter('assigned_vehicle_id'),
                    $customer_number
                );
            }
            try {
                $grant = (new subuser_grants_o())->add($customer_number, $subuser_id, (bool)$enabled, $note, $permissions);
                if ($assignedVehicleId !== null) {
                    $grant->assigned_vehicle_id->set($assignedVehicleId);
                }
                $response->success(['grant' => $grant->asArray()]);
            } catch (Exception $e) {
                $response->error('Failed to add subuser grant', 500);
            }
        }, [
            'manage_subuser_grants' => 'Create subuser grants for any customer (admin).',
            'add_own_subusers' => 'Create subuser grants for own customer. Subusers require node: SUBUSERS_ADD and X-Customer-Number header.'
        ]);

        $this->put('/subusers/grants', function () {
            global $response;
            /** Permissions (subuser-aware) */
            $permission_own = self::definePermission('edit_own_subusers', subusers_permission_node_key::SUBUSERS_EDIT);
            $permission_delete_own = self::definePermission('delete_own_subusers', subusers_permission_node_key::SUBUSERS_DELETE);
            $permission_other = self::definePermission('manage_subuser_grants');

            self::requireParameters(['id']);
            self::requireType(self::getParameter('id'), self::type_int());
            $grant = (new subuser_grants_o())->select((int)self::getParameter('id'));
            if (!$grant->exists()) {
                $response->error('Grant not found', 404);
            }

            $targetCustomer = (int)$grant->billing_customer_number->value();
            if (!self::hasPermission($permission_other, $targetCustomer)) {
                $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_EDIT, $targetCustomer);
            }

            if (self::isParametersSet(['enabled'])) {
                $enabledPreview = filter_var(self::getParameter('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
                if ($enabledPreview === false && !self::hasPermission($permission_other)) {
                    $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_DELETE, $targetCustomer);
                }
            }

            $templateAccess = $this->parseAccessTemplatePayload();
            if ($templateAccess !== null) {
                $grant->enabled->set($templateAccess['enabled']);
                $grant->permissions->set($templateAccess['permissions']);
            }

            // Update fields provided in the request
            if (self::isParametersSet(['enabled'])) {
                $enabled = (bool)filter_var(self::getParameter('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
                if ($enabled === null) { $enabled = false; }
                $grant->enabled->set($enabled);
            }
            if (self::isParametersSet(['note'])) {
                $note = (string)self::getParameter('note');
                self::requireType($note, self::type_string());
                self::requireMaxLength('note', 65535);
                $grant->note->set($note);
            }
            if (self::isParametersSet(['permissions'])) {
                $permissions = $this->parsePermissionsPayload(self::getParameter('permissions'), []);
                $grant->permissions->set($permissions);
            }
            if (self::isParametersSet(['assigned_vehicle_id'])) {
                $grant->assigned_vehicle_id->set($this->validateAssignedVehicleIdForCustomer(
                    self::getParameter('assigned_vehicle_id'),
                    $targetCustomer
                ));
            }
            $response->success($grant->asArray());
        },
            [
                'manage_subuser_grants' => 'Edit subuser grants for any customer (admin).',
                'edit_own_subusers' => 'Edit subuser grants for own customer. Subusers require node: SUBUSERS_EDIT and X-Customer-Number header.',
                'delete_own_subusers' => 'Disable subuser grants for own customer. Subusers require node: SUBUSERS_DELETE and X-Customer-Number header.'
            ]
        );

        $this->get('/subusers/permission-nodes', function () {
            global $response;
            $canUseGlobalManagement = self::hasPermission('manage_subuser_grants')
                || self::hasPermission('list_subusers')
                || self::hasPermission('add_subusers')
                || self::hasPermission('edit_subusers');
            if (!$canUseGlobalManagement) {
                $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_LIST);
            }

            // Build groups
            $groups = [
                new subusers_permission_nodes_bookings(),
                new subusers_permission_nodes_orders(),
                new subusers_permission_nodes_selfserve(),
                new subusers_permission_nodes_subusers(),
                new subusers_permission_nodes_vehicles(),
            ];
            $out = [];
            foreach ($groups as $group) {
                $nodes = [];
                foreach ($group->nodes as $key => $node) {
                    $nodes[] = [
                        'key' => $key,
                        'name' => $node->name,
                        'description' => $node->description,
                        'type' => $node->type->name,
                        'default' => (bool)($node->value ?? false),
                    ];
                }
                $out[] = [
                    'group' => $group->group_name,
                    'description' => $group->description,
                    'nodes' => $nodes,
                ];
            }
            $response->success($out);
        }, [
            'list_own_subusers' => 'List chauffeur permission nodes for own customer. Subusers require node: SUBUSERS_LIST and X-Customer-Number header.',
            'manage_subuser_grants' => 'List chauffeur permission nodes for administrative grant management.',
            'list_subusers' => 'List chauffeur permission nodes for superuser management.',
            'add_subusers' => 'List chauffeur permission nodes while inviting chauffeurs.',
            'edit_subusers' => 'List chauffeur permission nodes while editing chauffeur grants.',
        ]);

        $this->get('/subusers/permission-templates', function () {
            global $response;
            $canUseGlobalManagement = self::hasPermission('manage_subuser_grants')
                || self::hasPermission('list_subusers')
                || self::hasPermission('add_subusers')
                || self::hasPermission('edit_subusers');
            if (!$canUseGlobalManagement) {
                $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_LIST);
            }

            $response->success((new subuser_permission_templates_service())->accessModel());
        }, [
            'list_own_subusers' => 'List chauffeur permission templates for own customer. Subusers require node: SUBUSERS_LIST and X-Customer-Number header.',
            'manage_subuser_grants' => 'List chauffeur permission templates for administrative grant management.',
            'list_subusers' => 'List chauffeur permission templates for superuser management.',
            'add_subusers' => 'List chauffeur permission templates while inviting chauffeurs.',
            'edit_subusers' => 'List chauffeur permission templates while editing chauffeur grants.',
        ]);

        $this->post('/subusers', function () {
            global /** @var response $response */
            $response;
            self::requireParameters([
                'cvr',
                'phone_country_code',
                'phone'
            ]);

            $cvr = (int)self::getParameter('cvr');
            $phone_country_code =(int) self::getParameter('phone_country_code');
            $phone = (int)self::getParameter('phone');
            /** Validation */
            // Check if the CVR number is valid (8 digits)
            self::requireType($cvr, self::type_int());
            self::requireMinLength('cvr', 8);
            self::requireMaxLength('cvr', 8);
            // Check if the phone country code is valid (1-3 digits)
            self::requireType($phone_country_code, self::type_int());
            self::requireMinLength('phone_country_code', 1);
            self::requireMaxLength('phone_country_code', 3);
            // Check if the phone number is valid (4-15 digits)
            self::requireType($phone, self::type_int());
            self::requireMinLength('phone', 4);
            self::requireMaxLength('phone', 15);
            // Check if the CVR is registered to a company in the database
            $economic = new economic();
            $results = $economic->customers->customers->search([
                'corporateIdentificationNumber' => (string)$cvr,
            ],[
                'skipPages' => 0,
                'pageSize' => 1000, // Since the limit is 1000, we need to set the page size to 1000.
            ])->collection;
            if (count($results) === 0) {
                $response->error('Customer not found', 404);
            }
            /** At this point, we know that the CVR is valid and that the company exists in the database. */
            $company_user = (new users_o())->getUserByCustomerNumber((int)$results[0]->customerNumber);
            $company_user->requireSelected();
            /** We can now check if a subuser already exists with the same phone number. */
            $subuser = (new subusers_o())->getSubuserByPhone($phone_country_code, $phone);
            if ($subuser !== null) {
                $response->error('Account already exists with this phone number', 400);
            }
            /** We can now create the subuser and send a request to the company user to link the subuser to the company. */
            $subuser = (new subusers_o())->add(
                null,
                null,
                null,
                null,
                (int)$phone_country_code,
                (int)$phone
            );
            $invite = $this->issueSetupInvite($subuser);
            // Add the grant request
            $subuser_grants_o = new subuser_grants_o();
            try {
                $subuser_grants_o->add($results[0]->customerNumber, $subuser->id, false, null);
            } catch (Exception $e) {
                $response->error('Failed to add subuser grant', 500);
            }
            $response->success(['cvr' => $cvr, 'customer_number' => $results[0]->customerNumber, 'invite' => $invite]);
            // Code for creating a new subuser would go here
        });
        $this->get('/subusers/setup', function () {
            // Require the user to be logged in
            global $response;
            self::requireParameters(['token']);
            $token = self::getParameter('token');
            $setupThrottleKey = $this->recordThrottleAttempt('setup_token', 'token-validation', 20, 15 * 60);
            // Get the subuser with the setup token
            $subuser = (new subusers_o())->getSubuserBySetupToken($token);
            if ($subuser === null) {
                $response->error('Invalid or expired token', 400);
            }
            $this->clearThrottleAttempt($setupThrottleKey);
            $response->success(['message' => 'Token is valid', 'subuser_id' => $subuser->id]);
        });
        $this->post('/subusers/setup', function () {
            // Require the user to be logged in
            global $response;
            self::requireParameters(['token', 'password', 'name']);
            $token = (string)self::getParameter('token');
            $password = (string)self::getParameter('password');
            $name = (string)self::getParameter('name');
            $setupThrottleKey = $this->recordThrottleAttempt('setup_complete', 'token-complete', 20, 15 * 60);
            $this->requireSubuserPasswordPolicy($password);
            self::requireType($name, self::type_string());
            self::requireMinLength('name', 3);
            self::requireMaxLength('name', 255);
            /**
             * Optional fields:
             * - username: string, 3-255 characters
             * - email: string, valid email format, 3-255 characters
             */
            $username = null;
            $email = null;
            if (self::isParametersSet(['username'])) {
                $username = (string)self::getParameter('username');
                self::requireType($username, self::type_string());
                self::requireMinLength('username', 3);
                self::requireMaxLength('username', 255);
            }
            if (self::isParametersSet(['email'])) {
                $email = (string)self::getParameter('email');
                self::requireType($email, self::type_string());
                self::requireMinLength('email', 3);
                self::requireMaxLength('email', 255);
                if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                    $response->error('Invalid email format', 400);
                }
            }
            // Get the subuser with the setup token
            $subuser = (new subusers_o())->getSubuserBySetupToken($token);
            if ($subuser === null) {
                $response->error('Invalid or expired token', 400);
            }
            $this->assertSubuserIdentifiersAvailable(
                null,
                null,
                $username,
                $email,
                (int)$subuser->id
            );
            // Set the password for the subuser
            try {
                $setupUpdates = [
                    'password' => password_hash($password, PASSWORD_DEFAULT),
                    'name' => $name,
                    ...(!empty($username) ? ['username' => $username] : []),
                    ...(!empty($email) ? ['email' => $email] : []),
                ];
                $subuser->update($this->verificationService()->clearVerificationForChangedContacts($subuser, $setupUpdates));
                // Invalidate the setup token
                $subuser->invalidateSetupToken($token);
                $this->clearThrottleAttempt($setupThrottleKey);
                $response->success(['message' => 'Complete registration successful']);
            } catch (Exception $e) {
                $response->error($e->getMessage(), 400);
            }
        });

        $this->post('/subusers/auth/password', function () {
            global $response;
            /**
             * Username types:
             * 1. Phone number & country code
             * 2. Subuser id
             * 3. Username
             */
            $phone_country_code = null;
            $phone = null;
            $subuser_id = null;
            $username = null;
            if (self::isParametersSet(['phone_country_code', 'phone'])) {
                $phone_country_code = (int)self::getParameter('phone_country_code');
                $phone = (int)self::getParameter('phone');
                self::requireType($phone_country_code, self::type_int());
                self::requireMinLength('phone_country_code', 1);
                self::requireMaxLength('phone_country_code', 3);
                self::requireType($phone, self::type_int());
                self::requireMinLength('phone', 4);
                self::requireMaxLength('phone', 15);
            } elseif (self::isParametersSet(['subuser_id'])) {
                $subuser_id = (int)self::getParameter('subuser_id');
                self::requireType($subuser_id, self::type_int());
            } elseif (self::isParametersSet(['username'])) {
                $username = (string)self::getParameter('username');
                self::requireType($username, self::type_string());
                self::requireMinLength('username', 3);
                self::requireMaxLength('username', 255);
            } else {
                $response->error('You must provide either phone_country_code & phone, subuser_id or username', 400);
            }
            $identifier = $username !== null
                ? 'username:' . strtolower($username)
                : ($subuser_id !== null
                    ? 'id:' . (string)$subuser_id
                    : 'phone:' . (string)$phone_country_code . ':' . (string)$phone);
            $authThrottleKey = $this->recordThrottleAttempt('auth_password', $identifier, 10, 15 * 60);
            // Get the subuser based on the provided username type
            $subuser = null;
            if ($phone_country_code !== null && $phone !== null) {
                $subuser = (new subusers_o())->getSubuserByPhone($phone_country_code, $phone);
            } elseif ($subuser_id !== null) {
                $candidate = (new subusers_o())->select($subuser_id);
                if ($candidate->exists()) {
                    $candidate->getObjectProperties();
                    $subuser = $candidate;
                }
            } elseif ($username !== null) {
                $subuser = (new subusers_o())->getSubuserByUsername($username);
            }
            if ($subuser === null) {
                $this->subuserAuthFailure();
            }
            self::requireParameters(['password']);
            $password = (string)self::getParameter('password');
            try {
                $passwordHash = $subuser->password->value();
                if (is_string($passwordHash) && $passwordHash !== '' && password_verify($password, $passwordHash)) {
                    $this->clearThrottleAttempt($authThrottleKey);
                    if ($subuser->isTwoFactorEnabled()) {
                        $token = (new authentication())->create_2fa_token($subuser->id, '2FA_VERIFICATION_SUBUSER');
                        $response->success(['2fa_required' => true, '2fa_token' => $token]);
                    }
                    // Generate a new session for the subuser
                    $session = $subuser->generateSession();
                    $response->success(['session' => $session]);
                } else {
                    $this->subuserAuthFailure();
                }
            } catch (Exception $e) {
                $response->error($e->getMessage(), 500);
            }
        });

        // =============================
        // Subusers - List & Get (with grant visibility)
        // =============================
        $this->get('/superuser/subusers', function () {
            global $response;
            $this->requirePermission('list_subusers');
            $response->success($this->listSuperuserSubusers());
        }, [
            'list_subusers' => 'List all chauffeur access grants for superusers.',
        ]);

        $this->patch('/superuser/subusers/{subuser_id}', function () {
            global $response;

            $this->requirePermission('edit_subusers');
            $subuser = $this->loadSubuserOrFail($this->routePositiveInt('subuser_id'));
            $updates = $this->parseSuperuserSubuserProfileUpdates((int)$subuser->id);

            try {
                $subuser->update($this->verificationService()->clearVerificationForChangedContacts($subuser, $updates));
            } catch (Exception $exception) {
                $response->error($exception->getMessage(), 400);
            }

            $subuser = $this->loadSubuserOrFail((int)$subuser->id);
            $authUser = (new authentication())->get_user();
            (new logs_o())->add(
                'subusers',
                'global',
                1,
                $authUser !== false ? (int)$authUser->id : 0,
                'SUPERUSER_SUBUSER_PROFILE_UPDATE',
                'Updated chauffeur profile: ' . (int)$subuser->id
            );

            $response->success(['subuser' => $this->buildSubuserAccountPayload($subuser)]);
        }, [
            'edit_subusers' => 'Edit chauffeur account profile fields as a superuser.',
        ]);

        $this->post('/superuser/subusers/{subuser_id}/verification/{channel}/send', function () {
            global $response;

            $this->requirePermission('edit_subusers');
            $subuser = $this->loadSubuserOrFail($this->routePositiveInt('subuser_id'));
            $authUser = (new authentication())->get_user();
            $delivery = $this->sendContactVerificationCode(
                $subuser,
                (string)$this->fromRoute('channel'),
                $this->verificationActorContext('user', $authUser !== false ? (int)$authUser->id : 0)
            );
            $subuser = $this->loadSubuserOrFail((int)$subuser->id);
            $response->success([
                'delivery' => $delivery,
                'subuser' => $this->buildSubuserAccountPayload($subuser),
            ]);
        }, [
            'edit_subusers' => 'Send chauffeur contact verification codes as a superuser.',
        ]);

        $this->patch('/superuser/subusers/{subuser_id}/verification/{channel}', function () {
            global $response;

            $this->requirePermission('edit_subusers');
            self::requireParameters(['verified']);
            $verified = self::getParameter('verified');
            self::requireType($verified, self::type_bool());
            $subuser = $this->loadSubuserOrFail($this->routePositiveInt('subuser_id'));
            $authUser = (new authentication())->get_user();
            try {
                $result = $this->verificationService()->setVerificationState(
                    $subuser,
                    (string)$this->fromRoute('channel'),
                    (bool)$verified,
                    $this->verificationActorContext('user', $authUser !== false ? (int)$authUser->id : 0)
                );
            } catch (Exception $exception) {
                $response->error($exception->getMessage(), 400);
            }
            if (($result['status'] ?? null) === 'missing_destination') {
                $response->error((string)($result['message'] ?? 'No contact value is available for verification.'), 400);
            }

            $subuser = $this->loadSubuserOrFail((int)$subuser->id);
            $response->success([
                'result' => $result,
                'verification' => $this->verificationService()->status($subuser),
                'subuser' => $this->buildSubuserAccountPayload($subuser),
            ]);
        }, [
            'edit_subusers' => 'Set chauffeur contact verification state as a superuser.',
        ]);

        $this->post('/superuser/subusers/{subuser_id}/password', function () {
            global $response;

            $this->requirePermission('edit_subusers');
            self::requireParameters(['password']);

            $password = (string)self::getParameter('password');
            $this->requireSubuserPasswordPolicy($password);

            $subuser = $this->loadSubuserOrFail($this->routePositiveInt('subuser_id'));
            try {
                $subuser->setPassword($password);
                $subuser->invalidateCurrentSetupToken();
            } catch (Exception $exception) {
                $response->error($exception->getMessage(), 400);
            }

            $subuser = $this->loadSubuserOrFail((int)$subuser->id);
            $authUser = (new authentication())->get_user();
            (new logs_o())->add(
                'subusers',
                'global',
                1,
                $authUser !== false ? (int)$authUser->id : 0,
                'SUPERUSER_SUBUSER_PASSWORD_SET',
                'Set chauffeur password: ' . (int)$subuser->id
            );

            $response->success(['subuser' => $this->buildSubuserAccountPayload($subuser)]);
        }, [
            'edit_subusers' => 'Set a chauffeur account password as a superuser.',
        ]);

        $this->post('/superuser/subusers/{subuser_id}/login-link', function () {
            global $response;

            $this->requirePermission('edit_subusers');
            $this->requirePermission('SUPERUSER_INTIMIDATE');

            $subuser = $this->loadSubuserOrFail($this->routePositiveInt('subuser_id'));
            $response->success($this->createDirectSubuserLoginLinkPayload(
                $subuser,
                'SUPERUSER_SUBUSER_DIRECT_LOGIN_LINK',
                'Created chauffeur direct login link'
            ));
        }, [
            'edit_subusers' => 'Create chauffeur direct login links as a superuser.',
            'SUPERUSER_INTIMIDATE' => 'Create direct login sessions for chauffeur accounts.',
        ]);

        $this->post('/superuser/subusers/{subuser_id}/password-guide/{channel}/send', function () {
            $this->sendSuperuserSubuserDirectLoginLink('password_guide');
        }, [
            'edit_subusers' => 'Send chauffeur password guide links as a superuser.',
            'SUPERUSER_INTIMIDATE' => 'Create direct login sessions for chauffeur accounts.',
        ]);

        $this->post('/superuser/subusers/{subuser_id}/login-link/{channel}/send', function () {
            $this->sendSuperuserSubuserDirectLoginLink('login_link');
        }, [
            'edit_subusers' => 'Send chauffeur direct login links as a superuser.',
            'SUPERUSER_INTIMIDATE' => 'Create direct login sessions for chauffeur accounts.',
        ]);

        $this->get('/superuser/users/{user_id}/subusers', function () {
            global $response;
            $this->requirePermission('list_subusers');
            $targetUser = $this->resolveSuperuserSubuserTargetUser($this->routePositiveInt('user_id'));
            $this->addUserScopedSubuserMeta($targetUser);
            $response->success($this->listSuperuserSubusers((int)$targetUser['customer_number']));
        }, [
            'list_subusers' => 'List chauffeur access grants for a selected superuser customer account.',
        ]);

        $this->get('/superuser/users/{user_id}/subusers/summary', function () {
            global $response;
            $this->requirePermission('list_subusers');
            $targetUser = $this->resolveSuperuserSubuserTargetUser($this->routePositiveInt('user_id'));
            $response->add_meta('user_context', $targetUser);
            $response->success($this->buildSubuserSummaryForCustomer((int)$targetUser['customer_number']));
        }, [
            'list_subusers' => 'Summarize chauffeur access grants for a selected superuser customer account.',
        ]);

        $this->get('/subusers', function () {
            global $response;
            $customerNumber = $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_LIST);
            $customerName = $this->resolveCustomerName($customerNumber);
            $includeNonEnabled = false;
            if (self::isParametersSet(['include_non_enabled'])) {
                $tmp = filter_var(self::getParameter('include_non_enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
                $includeNonEnabled = $tmp === null ? false : (bool)$tmp;
            }

            $existsClause = sprintf(
                "EXISTS (SELECT 1 FROM `subuser_grants` sg WHERE sg.`subuser` = `subusers`.`id` AND %ssg.`deleted_at` IS NULL AND sg.`billing_customer_number` = %d)",
                $includeNonEnabled ? '' : 'sg.`enabled` = 1 AND ',
                $customerNumber
            );

            $objects = (new subusers_o())
                ->listObjectsWithPaginationIfSet(function ($o) use ($customerNumber, $customerName) {
                    $subuser = (new subusers_o())->select((int)$o['id']);
                    if (!$subuser->exists()) {
                        return null;
                    }
                    $subuser->getObjectProperties();
                    return $this->buildSubuserManagementPayload($subuser, $customerNumber, $customerName);
                }, null, [], $existsClause);

            if (is_array($objects)) {
                $objects = array_values(array_filter($objects, static fn ($item) => $item !== null));
            }

            $response->success($objects);
        }, [
            'list_own_subusers' => 'List chauffeurs for own customer. Subusers require node: SUBUSERS_LIST and X-Customer-Number header.',
        ]);

        $this->get('/subusers/me', function () {
            global $response;
            $subuser = (new authentication())->get_subuser();
            if ($subuser === false) {
                $response->error('Unauthorized', 401);
            }

            $response->success($this->buildCurrentSubuserPayload($subuser));
        }, []);

        $this->get('/subusers/me/verification', function () {
            global $response;
            $subuser = (new authentication())->get_subuser();
            if ($subuser === false) {
                $response->error('Unauthorized', 401);
            }

            $response->success($this->verificationService()->status($subuser));
        }, []);

        $this->post('/subusers/me/verification/{channel}/send', function () {
            global $response;
            $subuser = (new authentication())->get_subuser();
            if ($subuser === false) {
                $response->error('Unauthorized', 401);
            }

            $delivery = $this->sendContactVerificationCode(
                $subuser,
                (string)$this->fromRoute('channel'),
                $this->verificationActorContext('subuser', (int)$subuser->id)
            );
            $subuser = $this->loadSubuserOrFail((int)$subuser->id);
            $response->success([
                'delivery' => $delivery,
                'verification' => $this->verificationService()->status($subuser),
            ]);
        }, []);

        $this->post('/subusers/me/verification/{channel}/verify', function () {
            global $response;
            $subuser = (new authentication())->get_subuser();
            if ($subuser === false) {
                $response->error('Unauthorized', 401);
            }

            self::requireParameters(['code']);
            $result = $this->verifyContactVerificationCode(
                $subuser,
                (string)$this->fromRoute('channel'),
                (string)self::getParameter('code'),
                $this->verificationActorContext('subuser', (int)$subuser->id)
            );
            $subuser = $this->loadSubuserOrFail((int)$subuser->id);
            $response->success([
                'result' => $result,
                'verification' => $this->verificationService()->status($subuser),
                'subuser' => $this->buildCurrentSubuserPayload($subuser),
            ]);
        }, []);

        $this->post('/subusers/{subuser_id}/verification/{channel}/send', function () {
            global $response;

            $subuser = $this->loadSubuserOrFail($this->routePositiveInt('subuser_id'));
            $customerNumber = $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_EDIT);
            $grant = (new subuser_grants_o())->getGrantForSubuserAndCustomer((int)$subuser->id, $customerNumber, true);
            if ($grant === null) {
                $response->error('Subuser grant not found for selected customer', 404);
            }

            $authUser = (new authentication())->get_user();
            $delivery = $this->sendContactVerificationCode(
                $subuser,
                (string)$this->fromRoute('channel'),
                $this->verificationActorContext('user', $authUser !== false ? (int)$authUser->id : 0)
            );
            $subuser = $this->loadSubuserOrFail((int)$subuser->id);
            $response->success([
                'delivery' => $delivery,
                'subuser' => $this->buildSubuserManagementPayload($subuser, $customerNumber),
            ]);
        }, [
            'edit_own_subusers' => 'Send chauffeur contact verification codes for own-customer chauffeurs.',
        ]);

        $this->put('/subusers/me', function () {
            global $response;
            $subuser = (new authentication())->get_subuser();
            if ($subuser === false) {
                $response->error('Unauthorized', 401);
            }

            $updates = [];

            if (self::isParametersSet(['name'])) {
                $name = $this->normalizeOptionalString(self::getParameter('name'));
                if ($name === null || strlen($name) < 3 || strlen($name) > 255) {
                    $response->error('Name must be between 3 and 255 characters long', 400);
                }
                $updates['name'] = $name;
            }

            if (self::isParametersSet(['username'])) {
                $username = $this->normalizeOptionalString(self::getParameter('username'));
                if ($username !== null && (strlen($username) < 3 || strlen($username) > 50)) {
                    $response->error('Username must be between 3 and 50 characters long', 400);
                }
                $updates['username'] = $username;
            }

            if (self::isParametersSet(['email'])) {
                $email = $this->normalizeOptionalString(self::getParameter('email'));
                if ($email !== null && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
                    $response->error('Invalid email format', 400);
                }
                if ($email !== null && strlen($email) > 255) {
                    $response->error('Email must be at most 255 characters long', 400);
                }
                $updates['email'] = $email;
            }

            if (count($updates) === 0) {
                $response->error('No fields to update', 400);
            }

            $this->assertSubuserIdentifiersAvailable(
                null,
                null,
                $updates['username'] ?? null,
                $updates['email'] ?? null,
                (int)$subuser->id
            );

            try {
                $subuser->update($this->verificationService()->clearVerificationForChangedContacts($subuser, $updates));
            } catch (Exception $exception) {
                $response->error($exception->getMessage(), 400);
            }

            $subuser = (new subusers_o())->select((int)$subuser->id);
            $subuser->getObjectProperties();
            $response->success($this->buildCurrentSubuserPayload($subuser));
        }, []);

        $this->post('/superuser/subusers/invite', function () {
            self::requireParameters(['customer_number']);
            $this->requirePermission('add_subusers');
            $customerNumber = (int)self::getParameter('customer_number');
            self::requireType($customerNumber, self::type_int());
            $this->handleInviteSubuserForCustomer($customerNumber);
        }, [
            'add_subusers' => 'Invite or link chauffeurs for any customer (superuser).',
        ]);

        $this->post('/superuser/users/{user_id}/subusers/invite', function () {
            $this->requirePermission('add_subusers');
            $targetUser = $this->resolveSuperuserSubuserTargetUser($this->routePositiveInt('user_id'));
            $this->handleInviteSubuserForCustomer((int)$targetUser['customer_number']);
        }, [
            'add_subusers' => 'Invite or link chauffeurs for a selected superuser customer account.',
        ]);

        $this->post('/subusers/invite', function () {
            $customerNumber = $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_ADD);
            $this->handleInviteSubuserForCustomer($customerNumber);
        }, [
            'add_own_subusers' => 'Invite or link chauffeurs for own customer. Subusers require node: SUBUSERS_ADD and X-Customer-Number header.',
        ]);

        $this->post('/superuser/subusers/invite/resend', function () {
            global $response;

            $this->requirePermission('edit_subusers');
            self::requireParameters(['id', 'customer_number']);
            $subuserId = (int)self::getParameter('id');
            self::requireType($subuserId, self::type_int());
            $customerNumber = (int)self::getParameter('customer_number');
            self::requireType($customerNumber, self::type_int());

            $subuser = (new subusers_o())->select($subuserId);
            if (!$subuser->exists()) {
                $response->error('Subuser not found', 404);
            }
            $subuser->getObjectProperties();

            $grant = (new subuser_grants_o())->getGrantForSubuserAndCustomer($subuserId, $customerNumber, true);
            if ($grant === null) {
                $response->error('Subuser grant not found for selected customer', 404);
            }

            if (!$subuser->requiresSetup()) {
                $response->error('Driver account already accepted the invitation.', 409);
            }

            $invite = $this->issueSetupInvite($subuser);
            $response->success([
                'subuser' => $this->buildSubuserManagementPayload($subuser, $customerNumber),
                'grant' => $grant->asArray(),
                'invite' => $invite,
            ]);
        }, [
            'edit_subusers' => 'Resend chauffeur invites for a selected customer (superuser).',
        ]);

        $this->post('/superuser/users/{user_id}/subusers/{subuser_id}/invite/resend', function () {
            $this->requirePermission('edit_subusers');
            $targetUser = $this->resolveSuperuserSubuserTargetUser($this->routePositiveInt('user_id'));
            $this->resendInviteForScopedUser(
                $this->routePositiveInt('subuser_id'),
                (int)$targetUser['customer_number']
            );
        }, [
            'edit_subusers' => 'Resend chauffeur invites for a selected superuser customer account.',
        ]);

        $this->patch('/superuser/users/{user_id}/subusers/grants/{grant_id}', function () {
            $this->requirePermission('manage_subuser_grants');
            $targetUser = $this->resolveSuperuserSubuserTargetUser($this->routePositiveInt('user_id'));
            $this->patchScopedSubuserGrant(
                $this->routePositiveInt('grant_id'),
                (int)$targetUser['customer_number']
            );
        }, [
            'manage_subuser_grants' => 'Edit chauffeur grants for a selected superuser customer account.',
        ]);

        $this->post('/subusers/invite/resend', function () {
            global $response;

            self::requireParameters(['id']);
            $subuserId = (int)self::getParameter('id');
            self::requireType($subuserId, self::type_int());

            $customerNumber = $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_EDIT);
            $subuser = (new subusers_o())->select($subuserId);
            if (!$subuser->exists()) {
                $response->error('Subuser not found', 404);
            }
            $subuser->getObjectProperties();

            $grant = (new subuser_grants_o())->getGrantForSubuserAndCustomer($subuserId, $customerNumber, true);
            if ($grant === null) {
                $response->error('Subuser grant not found for selected customer', 404);
            }

            if (!$subuser->requiresSetup()) {
                $response->error('Driver account already accepted the invitation.', 409);
            }

            $invite = $this->issueSetupInvite($subuser);
            $response->success([
                'subuser' => $this->buildSubuserManagementPayload($subuser, $customerNumber),
                'grant' => $grant->asArray(),
                'invite' => $invite,
            ]);
        }, [
            'edit_own_subusers' => 'Resend chauffeur invite for own customer. Subusers require node: SUBUSERS_EDIT and X-Customer-Number header.',
        ]);

        $this->put('/subusers', function () {
            global $response;
            $this->requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_EDIT);
            $response->error('Customers can only manage subuser grants. Drivers own their account profile.', 403);
        }, [
            'edit_own_subusers' => 'Customers cannot edit chauffeur account profiles. They may only manage grants, permissions, and enabled state.',
        ]);
        // Public registration endpoint (alias of POST /subusers) matching OpenAPI: POST /subusers/me
        $this->post('/subusers/me', function () {
            global /** @var response $response */
            $response;
            // Required input
            self::requireParameters([
                'cvr',
                'phone_country_code',
                'phone'
            ]);

            $cvr = (int)self::getParameter('cvr');
            $phone_country_code = (int)self::getParameter('phone_country_code');
            $phone = (int)self::getParameter('phone');

            // Validation
            self::requireType($cvr, self::type_int());
            self::requireMinLength('cvr', 8);
            self::requireMaxLength('cvr', 8);
            self::requireType($phone_country_code, self::type_int());
            self::requireMinLength('phone_country_code', 1);
            self::requireMaxLength('phone_country_code', 3);
            self::requireType($phone, self::type_int());
            self::requireMinLength('phone', 4);
            self::requireMaxLength('phone', 15);

            // Look up company by CVR in e-conomic
            $economic = new economic();
            $results = $economic->customers->customers->search([
                'corporateIdentificationNumber' => (string)$cvr,
            ], [
                'skipPages' => 0,
                'pageSize' => 1000,
            ])->collection;
            if (count($results) === 0) {
                $response->error('Customer not found', 404);
            }

            // Ensure company user exists (sanity) and phone is not already registered
            $company_user = (new users_o())->getUserByCustomerNumber((int)$results[0]->customerNumber);
            $company_user->requireSelected();

            $existing = (new subusers_o())->getSubuserByPhone($phone_country_code, $phone);
            if ($existing !== null) {
                $response->error('Account already exists with this phone number', 400);
            }

            // Create subuser skeleton
            $subuser = (new subusers_o())->add(
                null,
                null,
                null,
                null,
                (int)$phone_country_code,
                (int)$phone
            );

            $invite = $this->issueSetupInvite($subuser);

            // Create a pending grant request for the company
            $subuser_grants_o = new subuser_grants_o();
            try {
                $subuser_grants_o->add($results[0]->customerNumber, $subuser->id, false, null);
            } catch (Exception $e) {
                $response->error('Failed to add subuser grant', 500);
            }

            $response->success(['cvr' => $cvr, 'customer_number' => $results[0]->customerNumber, 'invite' => $invite]);
        });
    }
}
