<?php

declare(strict_types=1);

usesApiSuite();

function subusers_without_customer_vehicles_deleted_at(callable $callback): void
{
    $db = api_test_runtime()->db();
    $column = $db->query("SHOW COLUMNS FROM `customer_vehicles` LIKE 'deleted_at'");
    if ($column === false) {
        throw new RuntimeException('Unable to inspect customer_vehicles.deleted_at test column.');
    }

    $hadColumn = (int)$column->num_rows > 0;
    if ($hadColumn) {
        $db->query('ALTER TABLE `customer_vehicles` DROP COLUMN `deleted_at`');
    }

    try {
        $callback();
    } finally {
        if ($hadColumn) {
            $db->query('ALTER TABLE `customer_vehicles` ADD COLUMN `deleted_at` DATETIME NULL');
        }
    }
}

const SUBUSER_PASSWORD_POLICY_MESSAGE = 'Password must contain at least one uppercase letter, one lowercase letter, and one number';

it('lists subusers when an existing grant has legacy zero permissions', function (): void {
    api_test_covers('GET /subusers', 'happy');

    $session = api_fixtures()->createUserSession(['list_own_subusers']);
    $subuser = api_fixtures()->createSubuser([
        'name' => 'Legacy Permission Driver',
    ]);
    $grantId = api_fixtures()->grantSubuser(
        $subuser['id'],
        $session['user']['customer_number'],
        ['VEHICLES_LIST']
    );

    $legacyPermissions = '0';
    $statement = api_test_runtime()->db()->prepare(
        'UPDATE `subuser_grants` SET `permissions` = ? WHERE `id` = ?'
    );
    $statement->bind_param('si', $legacyPermissions, $grantId);
    $statement->execute();
    $statement->close();

    $response = api_client()->get('/subusers?page=1&limit=5&include_non_enabled=true', $session['headers']);

    $response
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $matchingSubusers = array_values(array_filter(
        is_array($response->data()) ? $response->data() : [],
        static fn (mixed $item): bool => is_array($item) && (int)($item['id'] ?? 0) === (int)$subuser['id']
    ));

    expect($matchingSubusers)->toHaveCount(1);
    expect($matchingSubusers[0]['grant_permissions'] ?? null)->toBe([]);
    expect($matchingSubusers[0]['permissions'] ?? null)->toBe([]);
});

it('enforces the password policy for setup without rejecting legacy valid passwords at login', function (): void {
    api_test_covers('POST /subusers/setup', 'failure');
    api_test_covers('POST /subusers/auth/password', 'happy');

    $subuser = api_fixtures()->createSubuser([
        'password_plaintext' => 'invalidpassword',
    ]);

    $setupResponse = api_client()->post('/subusers/setup', [
        'token' => 'policy-test-token',
        'name' => 'Policy Driver',
        'password' => 'invalidpassword',
    ]);

    $authResponse = api_client()->post('/subusers/auth/password', [
        'subuser_id' => $subuser['id'],
        'password' => 'invalidpassword',
    ]);

    $setupResponse
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage(SUBUSER_PASSWORD_POLICY_MESSAGE);

    $authResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    expect($authResponse->data()['session'] ?? null)->toBeString();
    (new \objects\subusers_o())->invalidateSessionToken((string)$authResponse->data()['session']);
});

it('returns a generic error for invalid subuser credentials', function (): void {
    api_test_covers('POST /subusers/auth/password', 'failure');

    $response = api_client()->post('/subusers/auth/password', [
        'subuser_id' => 999999999,
        'password' => 'whatever',
    ]);

    $response
        ->assertStatus(401)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Invalid credentials');
});

it('lets drivers verify their email with a one-time code', function (): void {
    api_test_covers('GET /subusers/me/verification', 'happy');
    api_test_covers('POST /subusers/me/verification/{channel}/send', 'happy');
    api_test_covers('POST /subusers/me/verification/{channel}/verify', 'happy');
    api_test_covers('POST /subusers/me/verification/{channel}/verify', 'failure');

    $previousFakeMode = getenv('EMAIL_FAKE_MODE');
    $previousFakePath = getenv('EMAIL_FAKE_DELIVERIES_PATH');
    $fakePath = sys_get_temp_dir() . '/truckwash-subuser-verification-' . bin2hex(random_bytes(6)) . '.jsonl';

    putenv('EMAIL_FAKE_MODE=1');
    putenv('EMAIL_FAKE_DELIVERIES_PATH=' . $fakePath);
    \classes\email::resetFakeDeliveries();
    api_fixtures()->setModuleConfig('Email', 'mailersend_enabled', 'true', 'bool');
    api_test_runtime()->restartServer();

    try {
        $customer = api_fixtures()->createUser(['display_name' => 'Verification Customer']);
        $session = api_fixtures()->createSubuserSession(
            (int)$customer['customer_number'],
            ['VEHICLES_LIST'],
            [
                'name' => 'Verification Driver',
                'email' => 'driver.verify@example.test',
                'email_verified_at' => null,
                'phone_verified_at' => null,
            ]
        );

        $status = api_client()->get('/subusers/me/verification', $session['headers']);
        $send = api_client()->post('/subusers/me/verification/email/send', [], $session['headers']);

        $status
            ->assertStatus(200)
            ->assertEnvelope()
            ->assertSuccess();
        $send
            ->assertStatus(200)
            ->assertEnvelope()
            ->assertSuccess();

        expect($status->data()['email']['verified'] ?? null)->toBeFalse();
        expect($send->data()['delivery']['status'] ?? null)->toBe('sent');
        expect($send->data()['verification']['email']['verified'] ?? null)->toBeFalse();

        \classes\email::syncFakeDeliveries();
        $deliveries = \classes\email::$fake_deliveries;
        expect($deliveries)->toHaveCount(1);
        $message = (string)($deliveries[0]['message'] ?? '');
        preg_match('/\b([0-9]{6})\b/', $message, $matches);
        expect($matches[1] ?? null)->toBeString();

        $wrong = api_client()->post('/subusers/me/verification/email/verify', [
            'code' => '000000',
        ], $session['headers']);
        $wrong
            ->assertStatus(400)
            ->assertEnvelope()
            ->assertSuccess(false);

        $verify = api_client()->post('/subusers/me/verification/email/verify', [
            'code' => (string)$matches[1],
        ], $session['headers']);
        $verify
            ->assertStatus(200)
            ->assertEnvelope()
            ->assertSuccess();

        expect($verify->data()['verification']['email']['verified'] ?? null)->toBeTrue();
        expect($verify->data()['subuser']['email_verified'] ?? null)->toBeTrue();

        $row = api_fixtures()->fetchRowById('subusers', (int)$session['subuser']['id']);
        expect($row['email_verified_at'] ?? null)->not->toBeNull();
        expect($row['phone_verified_at'] ?? null)->toBeNull();
    } finally {
        api_test_runtime()->restartServer();
        \classes\email::resetFakeDeliveries();
        if ($previousFakeMode === false) {
            putenv('EMAIL_FAKE_MODE');
        } else {
            putenv('EMAIL_FAKE_MODE=' . $previousFakeMode);
        }
        if ($previousFakePath === false) {
            putenv('EMAIL_FAKE_DELIVERIES_PATH');
        } else {
            putenv('EMAIL_FAKE_DELIVERIES_PATH=' . $previousFakePath);
        }
        if (is_file($fakePath)) {
            unlink($fakePath);
        }
    }
});

it('clears email verification when a driver changes their email', function (): void {
    api_test_covers('PUT /subusers/me', 'happy');

    $customer = api_fixtures()->createUser(['display_name' => 'Driver Email Change Customer']);
    $session = api_fixtures()->createSubuserSession(
        (int)$customer['customer_number'],
        ['VEHICLES_LIST'],
        [
            'email' => 'old.driver.email@example.test',
            'email_verified_at' => '2026-07-13 10:00:00',
            'phone_verified_at' => '2026-07-13 10:00:00',
        ]
    );

    $response = api_client()->request('PUT', '/subusers/me', [
        'email' => 'new.driver.email@example.test',
    ], $session['headers']);

    $response
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    expect($response->data()['email'] ?? null)->toBe('new.driver.email@example.test');
    expect($response->data()['email_verified'] ?? null)->toBeFalse();
    expect($response->data()['phone_verified'] ?? null)->toBeTrue();

    $row = api_fixtures()->fetchRowById('subusers', (int)$session['subuser']['id']);
    expect($row['email_verified_at'] ?? null)->toBeNull();
    expect($row['phone_verified_at'] ?? null)->not->toBeNull();
});

it('requires subuser management access before exposing permission nodes', function (): void {
    api_test_covers('GET /subusers/permission-nodes', 'auth');

    $unauthenticated = api_client()->get('/subusers/permission-nodes');
    $unauthenticated
        ->assertStatus(401)
        ->assertEnvelope()
        ->assertSuccess(false);

    $session = api_fixtures()->createUserSession(['list_own_subusers']);
    $authorized = api_client()->get('/subusers/permission-nodes', $session['headers']);
    $authorized
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    expect($authorized->data())->toBeArray()->not->toBeEmpty();
});

it('requires subuser management access before exposing simplified permission templates', function (): void {
    api_test_covers('GET /subusers/permission-templates', 'auth');
    api_test_covers('GET /subusers/permission-templates', 'happy');

    $unauthenticated = api_client()->get('/subusers/permission-templates');
    $unauthenticated
        ->assertStatus(401)
        ->assertEnvelope()
        ->assertSuccess(false);

    $session = api_fixtures()->createUserSession(['list_own_subusers']);
    $authorized = api_client()->get('/subusers/permission-templates', $session['headers']);
    $authorized
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    expect($authorized->data()['templates'] ?? null)->toBeArray()->not->toBeEmpty();
    expect(array_column($authorized->data()['templates'], 'key'))
        ->toContain('driver')
        ->toContain('booking_coordinator')
        ->toContain('fleet_admin')
        ->toContain('deactivated');
    expect($authorized->data()['groups'] ?? null)->toBeArray()->not->toBeEmpty();
});

it('rejects customer subuser listing without own-scope permission', function (): void {
    api_test_covers('GET /subusers', 'auth');

    $session = api_fixtures()->createUserSession(['user']);
    $response = api_client()->get('/subusers?page=1&limit=5', $session['headers']);

    $response
        ->assertStatus(403)
        ->assertEnvelope()
        ->assertSuccess(false);
});

it('replaces older setup tokens when a new setup token is generated', function (): void {
    $subuser = api_fixtures()->createSubuser([
        'password_plaintext' => null,
        'name' => 'Pending Setup Driver',
    ]);
    $subuserObject = (new \objects\subusers_o())->select((int)$subuser['id']);
    $subuserObject->getObjectProperties();

    $firstToken = $subuserObject->generateSetupToken();
    $secondToken = $subuserObject->generateSetupToken();

    try {
        expect((new \objects\subusers_o())->getSubuserBySetupToken($firstToken))->toBeNull();
        expect((new \objects\subusers_o())->getSubuserBySetupToken($secondToken))->not->toBeNull();
    } finally {
        (new \objects\subusers_o())->invalidateSetupToken($secondToken);
    }
});

it('validates setup identifiers before setting the driver password', function (): void {
    api_test_covers('POST /subusers/setup', 'failure');

    $existing = api_fixtures()->createSubuser([
        'username' => 'existing-driver-setup',
    ]);
    $pending = api_fixtures()->createSubuser([
        'password_plaintext' => null,
        'username' => 'pending-driver-setup',
        'name' => 'Pending Driver',
    ]);
    $pendingObject = (new \objects\subusers_o())->select((int)$pending['id']);
    $pendingObject->getObjectProperties();
    $token = $pendingObject->generateSetupToken();

    try {
        $response = api_client()->post('/subusers/setup', [
            'token' => $token,
            'name' => 'Pending Driver',
            'username' => $existing['username'],
            'password' => 'ValidPass123',
        ]);

        $response
            ->assertStatus(400)
            ->assertEnvelope()
            ->assertSuccess(false)
            ->assertMessage('Account already exists with this username');

        $row = api_fixtures()->fetchRowById('subusers', (int)$pending['id']);
        expect($row['password'] ?? null)->toBeNull();
        expect((new \objects\subusers_o())->getSubuserBySetupToken($token))->not->toBeNull();
    } finally {
        (new \objects\subusers_o())->invalidateSetupToken($token);
    }
});

it('lists chauffeurs once with grouped grants across customers for superusers', function (): void {
    $session = api_fixtures()->createUserSession(['list_subusers']);
    $firstCustomer = api_fixtures()->createUser([
        'display_name' => 'Fleet Customer Alpha',
        'economic_customer_name' => 'Fleet Customer Alpha',
    ]);
    $secondCustomer = api_fixtures()->createUser([
        'display_name' => 'Fleet Customer Beta',
        'economic_customer_name' => 'Fleet Customer Beta',
    ]);
    $firstSubuser = api_fixtures()->createSubuser(['name' => 'Alpha Driver']);
    $secondSubuser = api_fixtures()->createSubuser(['name' => 'Beta Driver']);
    $firstVehicle = api_fixtures()->createVehicle([
        'customer_id' => (int)$firstCustomer['customer_number'],
        'type' => 1,
        'reg' => 'ab12345',
    ]);
    $firstGrantId = api_fixtures()->grantSubuser(
        (int)$firstSubuser['id'],
        (int)$firstCustomer['customer_number'],
        ['VEHICLES_LIST', 'SUBUSERS_LIST', 'SELFSERVE_LIST', 'SELFSERVE_ADD'],
        ['assigned_vehicle_id' => (int)$firstVehicle['id']]
    );
    $secondGrantId = api_fixtures()->grantSubuser(
        (int)$secondSubuser['id'],
        (int)$secondCustomer['customer_number'],
        ['BOOKINGS_LIST']
    );
    $sharedGrantId = api_fixtures()->grantSubuser(
        (int)$firstSubuser['id'],
        (int)$secondCustomer['customer_number'],
        ['ORDERS_LIST']
    );

    $response = api_client()->get('/superuser/subusers?page=1&limit=20&search=Driver', $session['headers']);

    $response
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $rows = array_values(array_filter(
        is_array($response->data()) ? $response->data() : [],
        static fn (mixed $item): bool => is_array($item)
            && in_array((int)($item['id'] ?? 0), [(int)$firstSubuser['id'], (int)$secondSubuser['id']], true)
    ));

    expect($rows)->toHaveCount(2);
    expect($response->meta()['pagination']['total'] ?? null)->toBe(2);

    $bySubuserId = [];
    foreach ($rows as $row) {
        $bySubuserId[(int)$row['id']] = $row;
    }

    expect($bySubuserId)->toHaveKeys([(int)$firstSubuser['id'], (int)$secondSubuser['id']]);

    $firstRow = $bySubuserId[(int)$firstSubuser['id']];
    expect($firstRow['grant_count'] ?? null)->toBe(2);
    expect($firstRow['customer_numbers'] ?? [])->toContain((int)$firstCustomer['customer_number']);
    expect($firstRow['customer_numbers'] ?? [])->toContain((int)$secondCustomer['customer_number']);

    $firstGrantsById = [];
    foreach ($firstRow['grants'] ?? [] as $grant) {
        $firstGrantsById[(int)$grant['grant_id']] = $grant;
    }

    expect($firstGrantsById[$firstGrantId]['customer_number'])->toBe((int)$firstCustomer['customer_number']);
    expect($firstGrantsById[$firstGrantId]['assigned_vehicle_id'])->toBe((int)$firstVehicle['id']);
    expect($firstGrantsById[$firstGrantId]['assigned_vehicle_reg'])->toBe('AB12345');
    expect($firstGrantsById[$firstGrantId]['dognvask_enabled'])->toBeTrue();
    expect($firstGrantsById[$sharedGrantId]['customer_number'])->toBe((int)$secondCustomer['customer_number']);
    expect($firstGrantsById[$sharedGrantId]['dognvask_enabled'])->toBeFalse();

    $secondRow = $bySubuserId[(int)$secondSubuser['id']];
    expect($secondRow['grant_count'] ?? null)->toBe(1);
    expect($secondRow['grants'][0]['grant_id'] ?? null)->toBe($secondGrantId);
    expect($secondRow['grants'][0]['customer_name'] ?? null)->toBe('Fleet Customer Beta');
});

it('lists superuser chauffeurs with assigned vehicles when customer vehicles has no deleted at column', function (): void {
    subusers_without_customer_vehicles_deleted_at(function (): void {
        api_test_covers('GET /superuser/subusers', 'happy');
        api_test_covers('GET /superuser/users/{user_id}/subusers', 'happy');

        $customerVehiclesDeletedAtColumn = api_test_runtime()->db()->query(
            "SHOW COLUMNS FROM `customer_vehicles` LIKE 'deleted_at'"
        );
        expect($customerVehiclesDeletedAtColumn)->not->toBeFalse();
        expect((int)$customerVehiclesDeletedAtColumn->num_rows)->toBe(0);

        $session = api_fixtures()->createUserSession(['list_subusers']);
        $suffix = (string)random_int(100000, 999999);
        $customer = api_fixtures()->createUser([
            'display_name' => 'Legacy Subuser Vehicle Customer ' . $suffix,
            'economic_customer_name' => 'Legacy Subuser Vehicle Customer ' . $suffix,
        ]);
        $subuser = api_fixtures()->createSubuser([
            'name' => 'Legacy Vehicle Driver ' . $suffix,
        ]);
        $vehicle = api_fixtures()->createVehicle([
            'customer_id' => (int)$customer['customer_number'],
            'type' => 1,
            'reg' => 'legacy' . $suffix,
        ]);
        $grantId = api_fixtures()->grantSubuser(
            (int)$subuser['id'],
            (int)$customer['customer_number'],
            ['VEHICLES_LIST'],
            ['assigned_vehicle_id' => (int)$vehicle['id']]
        );

        $list = api_client()->get('/superuser/subusers?page=1&limit=20', $session['headers']);
        $search = api_client()->get(
            '/superuser/subusers?page=1&limit=20&search=' . rawurlencode((string)$vehicle['reg']),
            $session['headers']
        );
        $scoped = api_client()->get(
            '/superuser/users/' . $customer['id'] . '/subusers?page=1&limit=20',
            $session['headers']
        );

        foreach ([$list, $search, $scoped] as $response) {
            $response
                ->assertStatus(200)
                ->assertEnvelope()
                ->assertSuccess();
        }

        $matchingSearchRows = array_values(array_filter(
            is_array($search->data()) ? $search->data() : [],
            static fn (mixed $item): bool => is_array($item) && (int)($item['id'] ?? 0) === (int)$subuser['id']
        ));
        expect($matchingSearchRows)->toHaveCount(1);

        $matchingScopedRows = array_values(array_filter(
            is_array($scoped->data()) ? $scoped->data() : [],
            static fn (mixed $item): bool => is_array($item) && (int)($item['id'] ?? 0) === (int)$subuser['id']
        ));
        expect($matchingScopedRows)->toHaveCount(1);

        $grantsById = [];
        foreach ($matchingScopedRows[0]['grants'] ?? [] as $grant) {
            $grantsById[(int)$grant['grant_id']] = $grant;
        }

        expect($grantsById)->toHaveKey($grantId);
        expect($grantsById[$grantId]['assigned_vehicle_id'])->toBe((int)$vehicle['id']);
        expect($grantsById[$grantId]['assigned_vehicle_reg'])->toBe(strtoupper((string)$vehicle['reg']));
        expect($scoped->meta()['pagination']['total'] ?? null)->toBeGreaterThanOrEqual(1);
    });
});

it('paginates superuser chauffeur lists by unique chauffeur instead of grant count', function (): void {
    $session = api_fixtures()->createUserSession(['list_subusers']);
    $suffix = (string)random_int(100000, 999999);
    $firstCustomer = api_fixtures()->createUser(['display_name' => 'Grouped Page Alpha ' . $suffix]);
    $secondCustomer = api_fixtures()->createUser(['display_name' => 'Grouped Page Beta ' . $suffix]);
    $firstSubuser = api_fixtures()->createSubuser(['name' => 'Grouped Page Driver Alpha ' . $suffix]);
    $secondSubuser = api_fixtures()->createSubuser(['name' => 'Grouped Page Driver Beta ' . $suffix]);

    api_fixtures()->grantSubuser((int)$firstSubuser['id'], (int)$firstCustomer['customer_number'], ['BOOKINGS_LIST']);
    api_fixtures()->grantSubuser((int)$firstSubuser['id'], (int)$secondCustomer['customer_number'], ['ORDERS_LIST']);
    api_fixtures()->grantSubuser((int)$secondSubuser['id'], (int)$secondCustomer['customer_number'], ['VEHICLES_LIST']);

    $pageOne = api_client()->get(
        '/superuser/subusers?page=1&limit=1&search=' . rawurlencode($suffix) . '&order=name:ASC',
        $session['headers']
    );
    $pageTwo = api_client()->get(
        '/superuser/subusers?page=2&limit=1&search=' . rawurlencode($suffix) . '&order=name:ASC',
        $session['headers']
    );

    $pageOne
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();
    $pageTwo
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    expect($pageOne->meta()['pagination']['total'] ?? null)->toBe(2);
    expect($pageTwo->meta()['pagination']['total'] ?? null)->toBe(2);
    expect($pageOne->data())->toHaveCount(1);
    expect($pageTwo->data())->toHaveCount(1);
    expect($pageOne->data()[0]['id'] ?? null)->toBe((int)$firstSubuser['id']);
    expect($pageOne->data()[0]['grant_count'] ?? null)->toBe(2);
    expect($pageTwo->data()[0]['id'] ?? null)->toBe((int)$secondSubuser['id']);
    expect($pageTwo->data()[0]['grant_count'] ?? null)->toBe(1);
});

it('lets superusers invite chauffeurs for a selected customer', function (): void {
    $session = api_fixtures()->createUserSession(['add_subusers']);
    $customer = api_fixtures()->createUser([
        'display_name' => 'Invite Target Customer',
        'economic_customer_name' => 'Invite Target Customer',
    ]);
    $phone = 71000000 + ((int)$customer['customer_number'] % 1000000);
    $createdSubuserId = null;
    $createdGrantId = null;
    $setupToken = null;

    try {
        $response = api_client()->post('/superuser/subusers/invite', [
            'customer_number' => (int)$customer['customer_number'],
            'name' => 'Invited Driver',
            'phone_country_code' => 45,
            'phone' => $phone,
            'permissions' => ['VEHICLES_LIST'],
            'note' => 'Created by superuser test',
        ], $session['headers']);

        $response
            ->assertStatus(200)
            ->assertEnvelope()
            ->assertSuccess();

        $payload = $response->data();
        $createdSubuserId = isset($payload['subuser']['id']) ? (int)$payload['subuser']['id'] : null;
        $createdGrantId = isset($payload['grant']['id']) ? (int)$payload['grant']['id'] : null;
        $setupToken = isset($payload['invite']['setup_token']) ? (string)$payload['invite']['setup_token'] : null;

        expect($payload['subuser']['customer_number'] ?? null)->toBe((int)$customer['customer_number']);
        expect($payload['subuser']['name'] ?? null)->toBe('Invited Driver');
        expect($payload['subuser']['grant_permissions'] ?? null)->toBe(['VEHICLES_LIST']);
        expect($payload['grant']['note'] ?? null)->toBe('Created by superuser test');
        expect($payload['invite']['setup_link'] ?? null)->toBeString();
    } finally {
        if ($setupToken !== null && $setupToken !== '') {
            (new \objects\subusers_o())->invalidateSetupToken($setupToken);
        }
        if ($createdGrantId !== null) {
            api_test_runtime()->db()->query('DELETE FROM `subuser_grants` WHERE `id` = ' . $createdGrantId);
        }
        if ($createdSubuserId !== null) {
            api_test_runtime()->db()->query('DELETE FROM `tokens` WHERE `user_id` = ' . $createdSubuserId . " AND `type` = 'AUTH_TOKEN_SUBUSER'");
            api_test_runtime()->db()->query('DELETE FROM `subusers` WHERE `id` = ' . $createdSubuserId);
        }
    }
});

it('applies simplified driver access templates when inviting and updating chauffeur grants', function (): void {
    api_test_covers('POST /superuser/subusers/invite', 'happy');
    api_test_covers('PATCH /superuser/users/{user_id}/subusers/grants/{grant_id}', 'happy');
    api_test_covers('PATCH /superuser/users/{user_id}/subusers/grants/{grant_id}', 'failure');

    $session = api_fixtures()->createUserSession(['add_subusers', 'manage_subuser_grants']);
    $customer = api_fixtures()->createUser(['display_name' => 'Template Target Customer']);
    $phone = 72000000 + ((int)$customer['customer_number'] % 1000000);
    $createdSubuserId = null;
    $createdGrantId = null;
    $setupToken = null;

    try {
        $invite = api_client()->post('/superuser/subusers/invite', [
            'customer_number' => (int)$customer['customer_number'],
            'name' => 'Template Driver',
            'phone_country_code' => 45,
            'phone' => $phone,
            'permission_template_key' => 'booking_coordinator',
        ], $session['headers']);

        $invite
            ->assertStatus(200)
            ->assertEnvelope()
            ->assertSuccess();

        $payload = $invite->data();
        $createdSubuserId = isset($payload['subuser']['id']) ? (int)$payload['subuser']['id'] : null;
        $createdGrantId = isset($payload['grant']['id']) ? (int)$payload['grant']['id'] : null;
        $setupToken = isset($payload['invite']['setup_token']) ? (string)$payload['invite']['setup_token'] : null;

        expect($payload['subuser']['permission_template_key'] ?? null)->toBe('booking_coordinator');
        expect($payload['subuser']['grant_permissions'] ?? null)
            ->toContain('BOOKINGS_EDIT')
            ->toContain('BOOKINGS_ADD')
            ->not->toContain('SUBUSERS_ADD');

        $deactivate = api_client()->request(
            'PATCH',
            '/superuser/users/' . $customer['id'] . '/subusers/grants/' . $createdGrantId,
            ['permission_template_key' => 'deactivated'],
            $session['headers']
        );

        $deactivate
            ->assertStatus(200)
            ->assertEnvelope()
            ->assertSuccess();

        expect($deactivate->data()['subuser']['permission_template_key'] ?? null)->toBe('deactivated');
        expect($deactivate->data()['grant']['enabled'] ?? null)->toBeFalse();
        expect($deactivate->data()['grant']['permissions'] ?? null)->toBe([]);

        $invalid = api_client()->request(
            'PATCH',
            '/superuser/users/' . $customer['id'] . '/subusers/grants/' . $createdGrantId,
            ['permission_template_key' => 'unknown_template'],
            $session['headers']
        );

        $invalid
            ->assertStatus(400)
            ->assertEnvelope()
            ->assertSuccess(false);
    } finally {
        if ($setupToken !== null && $setupToken !== '') {
            (new \objects\subusers_o())->invalidateSetupToken($setupToken);
        }
        if ($createdGrantId !== null) {
            api_test_runtime()->db()->query('DELETE FROM `subuser_grants` WHERE `id` = ' . $createdGrantId);
        }
        if ($createdSubuserId !== null) {
            api_test_runtime()->db()->query('DELETE FROM `tokens` WHERE `user_id` = ' . $createdSubuserId . " AND `type` = 'AUTH_TOKEN_SUBUSER'");
            api_test_runtime()->db()->query('DELETE FROM `subusers` WHERE `id` = ' . $createdSubuserId);
        }
    }
});

it('lists and summarizes chauffeurs through the user-scoped superuser route', function (): void {
    $session = api_fixtures()->createUserSession(['list_subusers']);
    $targetCustomer = api_fixtures()->createUser([
        'display_name' => 'Scoped Customer Alpha',
        'economic_customer_name' => 'Scoped Customer Alpha',
    ]);
    $otherCustomer = api_fixtures()->createUser([
        'display_name' => 'Scoped Customer Beta',
        'economic_customer_name' => 'Scoped Customer Beta',
    ]);
    $activeSubuser = api_fixtures()->createSubuser(['name' => 'Scoped Active Driver']);
    $pendingSubuser = api_fixtures()->createSubuser([
        'name' => 'Scoped Pending Driver',
        'password_plaintext' => null,
    ]);
    $disabledSubuser = api_fixtures()->createSubuser(['name' => 'Scoped Disabled Driver']);
    $otherSubuser = api_fixtures()->createSubuser(['name' => 'Scoped Other Driver']);

    $activeGrantId = api_fixtures()->grantSubuser(
        (int)$activeSubuser['id'],
        (int)$targetCustomer['customer_number'],
        ['VEHICLES_LIST']
    );
    api_fixtures()->grantSubuser(
        (int)$pendingSubuser['id'],
        (int)$targetCustomer['customer_number'],
        ['BOOKINGS_LIST']
    );
    $disabledGrantId = api_fixtures()->grantSubuser(
        (int)$disabledSubuser['id'],
        (int)$targetCustomer['customer_number'],
        ['ORDERS_LIST']
    );
    api_test_runtime()->db()->query('UPDATE `subuser_grants` SET `enabled` = 0 WHERE `id` = ' . $disabledGrantId);
    api_fixtures()->grantSubuser(
        (int)$otherSubuser['id'],
        (int)$otherCustomer['customer_number'],
        ['SELFSERVE_LIST']
    );

    $response = api_client()->get(
        '/superuser/users/' . $targetCustomer['id'] . '/subusers?page=1&limit=20&include_non_enabled=true',
        $session['headers']
    );
    $summary = api_client()->get(
        '/superuser/users/' . $targetCustomer['id'] . '/subusers/summary',
        $session['headers']
    );

    $response
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();
    $summary
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $rows = is_array($response->data()) ? $response->data() : [];
    $grantIds = array_map(static fn (array $row): int => (int)($row['grant_id'] ?? 0), $rows);

    expect($grantIds)->toContain($activeGrantId);
    expect($grantIds)->not->toContain(0);
    expect($rows)->toHaveCount(3);
    foreach ($rows as $row) {
        expect($row['customer_number'] ?? null)->toBe((int)$targetCustomer['customer_number']);
    }

    expect($response->meta()['user_context']['user_id'] ?? null)->toBe((int)$targetCustomer['id']);
    expect($response->meta()['subusers_summary'] ?? null)->toMatchArray([
        'total' => 3,
        'active' => 1,
        'pending_setup' => 1,
        'disabled' => 1,
    ]);
    expect($summary->data())->toMatchArray([
        'total' => 3,
        'active' => 1,
        'pending_setup' => 1,
        'disabled' => 1,
    ]);
});

it('edits only customer-matching chauffeur grants through the user-scoped route', function (): void {
    $session = api_fixtures()->createUserSession(['manage_subuser_grants']);
    $targetCustomer = api_fixtures()->createUser(['display_name' => 'Scoped Patch Customer']);
    $otherCustomer = api_fixtures()->createUser(['display_name' => 'Scoped Patch Other']);
    $targetSubuser = api_fixtures()->createSubuser(['name' => 'Patch Target Driver']);
    $otherSubuser = api_fixtures()->createSubuser(['name' => 'Patch Other Driver']);
    $targetVehicle = api_fixtures()->createVehicle([
        'customer_id' => (int)$targetCustomer['customer_number'],
        'type' => 1,
        'reg' => 'scope123',
    ]);
    $otherVehicle = api_fixtures()->createVehicle([
        'customer_id' => (int)$otherCustomer['customer_number'],
        'type' => 1,
        'reg' => 'other123',
    ]);
    $targetGrantId = api_fixtures()->grantSubuser(
        (int)$targetSubuser['id'],
        (int)$targetCustomer['customer_number'],
        ['VEHICLES_LIST']
    );
    $otherGrantId = api_fixtures()->grantSubuser(
        (int)$otherSubuser['id'],
        (int)$otherCustomer['customer_number'],
        ['BOOKINGS_LIST']
    );

    $update = api_client()->request(
        'PATCH',
        '/superuser/users/' . $targetCustomer['id'] . '/subusers/grants/' . $targetGrantId,
        [
            'enabled' => false,
            'note' => 'Scoped note',
            'permissions' => ['ORDERS_LIST'],
            'assigned_vehicle_id' => (int)$targetVehicle['id'],
        ],
        $session['headers']
    );
    $mismatchedVehicle = api_client()->request(
        'PATCH',
        '/superuser/users/' . $targetCustomer['id'] . '/subusers/grants/' . $targetGrantId,
        ['assigned_vehicle_id' => (int)$otherVehicle['id']],
        $session['headers']
    );
    $crossCustomer = api_client()->request(
        'PATCH',
        '/superuser/users/' . $targetCustomer['id'] . '/subusers/grants/' . $otherGrantId,
        ['note' => 'Should not save'],
        $session['headers']
    );

    $update
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();
    $mismatchedVehicle
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false);
    $crossCustomer
        ->assertStatus(404)
        ->assertEnvelope()
        ->assertSuccess(false);

    expect($update->data()['grant']['enabled'] ?? null)->toBeFalse();
    expect($update->data()['grant']['note'] ?? null)->toBe('Scoped note');
    expect($update->data()['grant']['permissions'] ?? null)->toBe(['ORDERS_LIST']);
    expect($update->data()['grant']['assigned_vehicle_id'] ?? null)->toBe((int)$targetVehicle['id']);
    expect($update->data()['subuser']['assigned_vehicle_reg'] ?? null)->toBe('SCOPE123');
});

it('lets superusers edit chauffeur account details and set passwords', function (): void {
    api_test_covers('PATCH /superuser/subusers/{subuser_id}', 'happy');
    api_test_covers('POST /superuser/subusers/{subuser_id}/password', 'happy');

    $session = api_fixtures()->createUserSession(['edit_subusers']);
    $subuser = api_fixtures()->createSubuser([
        'password_plaintext' => null,
        'name' => 'Admin Managed Driver',
        'email' => null,
        'phone_country_code' => 45,
        'phone' => 73123456,
        'email_verified_at' => '2026-07-13 10:00:00',
        'phone_verified_at' => '2026-07-13 10:00:00',
    ]);
    $subuserObject = (new \objects\subusers_o())->select((int)$subuser['id']);
    $subuserObject->getObjectProperties();
    $setupToken = $subuserObject->generateSetupToken();

    try {
        $profile = api_client()->request(
            'PATCH',
            '/superuser/subusers/' . $subuser['id'],
            [
                'name' => 'Admin Updated Driver',
                'email' => 'admin.updated.driver@example.com',
                'phone_country_code' => 46,
                'phone' => 73123457,
            ],
            $session['headers']
        );

        $password = api_client()->post(
            '/superuser/subusers/' . $subuser['id'] . '/password',
            ['password' => 'ValidPass123'],
            $session['headers']
        );

        $profile
            ->assertStatus(200)
            ->assertEnvelope()
            ->assertSuccess();
        $password
            ->assertStatus(200)
            ->assertEnvelope()
            ->assertSuccess();

        expect($profile->data()['subuser']['name'] ?? null)->toBe('Admin Updated Driver');
        expect($profile->data()['subuser']['email'] ?? null)->toBe('admin.updated.driver@example.com');
        expect($profile->data()['subuser']['phone_country_code'] ?? null)->toBe(46);
        expect($profile->data()['subuser']['phone'] ?? null)->toBe(73123457);
        expect($profile->data()['subuser']['email_verified'] ?? null)->toBeFalse();
        expect($profile->data()['subuser']['phone_verified'] ?? null)->toBeFalse();
        expect($password->data()['subuser']['setup_required'] ?? null)->toBeFalse();
        expect((new \objects\subusers_o())->getSubuserBySetupToken($setupToken))->toBeNull();

        $row = api_fixtures()->fetchRowById('subusers', (int)$subuser['id']);
        expect(password_verify('ValidPass123', (string)($row['password'] ?? '')))->toBeTrue();
        expect($row['email_verified_at'] ?? null)->toBeNull();
        expect($row['phone_verified_at'] ?? null)->toBeNull();
    } finally {
        (new \objects\subusers_o())->invalidateSetupToken($setupToken);
    }
});

it('creates direct chauffeur login links scoped to a selected customer grant', function (): void {
    api_test_covers('POST /superuser/subusers/{subuser_id}/login-link', 'happy');
    api_test_covers('POST /superuser/subusers/{subuser_id}/login-link', 'failure');

    $session = api_fixtures()->createUserSession(['edit_subusers', 'SUPERUSER_INTIMIDATE']);
    $firstCustomer = api_fixtures()->createUser(['display_name' => 'Direct Login Customer Alpha']);
    $secondCustomer = api_fixtures()->createUser(['display_name' => 'Direct Login Customer Beta']);
    $subuser = api_fixtures()->createSubuser(['name' => 'Direct Login Driver']);
    api_fixtures()->grantSubuser(
        (int)$subuser['id'],
        (int)$firstCustomer['customer_number'],
        ['VEHICLES_LIST']
    );
    $secondGrantId = api_fixtures()->grantSubuser(
        (int)$subuser['id'],
        (int)$secondCustomer['customer_number'],
        ['ORDERS_LIST']
    );

    $ambiguous = api_client()->post(
        '/superuser/subusers/' . $subuser['id'] . '/login-link',
        [],
        $session['headers']
    );
    $direct = api_client()->post(
        '/superuser/subusers/' . $subuser['id'] . '/login-link',
        ['grant_id' => $secondGrantId],
        $session['headers']
    );

    $ambiguous
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Customer number or grant id is required for drivers with multiple customer grants');
    $direct
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    expect($direct->data()['customer_number'] ?? null)->toBe((int)$secondCustomer['customer_number']);
    expect($direct->data()['login_path'] ?? null)->toBeString();

    $parts = parse_url((string)$direct->data()['login_path']);
    parse_str((string)($parts['query'] ?? ''), $query);

    expect($parts['path'] ?? null)->toBe('/login/qr');
    expect($query['type'] ?? null)->toBe('subuser');
    expect((int)($query['customer_number'] ?? 0))->toBe((int)$secondCustomer['customer_number']);
    expect($query['token'] ?? null)->toBeString();

    $resolved = (new \objects\subusers_o())->getSubuserBySessionToken((string)$query['token']);
    expect($resolved)->not->toBeNull();
    expect((int)$resolved->id)->toBe((int)$subuser['id']);
    (new \objects\subusers_o())->invalidateSessionToken((string)$query['token']);
});

it('rejects mismatched customer numbers on user-scoped chauffeur invites', function (): void {
    $session = api_fixtures()->createUserSession(['add_subusers']);
    $targetCustomer = api_fixtures()->createUser(['display_name' => 'Scoped Invite Customer']);
    $otherCustomer = api_fixtures()->createUser(['display_name' => 'Scoped Invite Other']);

    $response = api_client()->post('/superuser/users/' . $targetCustomer['id'] . '/subusers/invite', [
        'customer_number' => (int)$otherCustomer['customer_number'],
        'name' => 'Mismatched Driver',
        'phone_country_code' => 45,
        'phone' => 71999999,
    ], $session['headers']);

    $response
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Customer number does not match selected user');
});

it('resends pending user-scoped chauffeur invites and blocks accepted accounts', function (): void {
    $session = api_fixtures()->createUserSession(['edit_subusers']);
    $targetCustomer = api_fixtures()->createUser(['display_name' => 'Scoped Resend Customer']);
    $pendingSubuser = api_fixtures()->createSubuser([
        'name' => 'Pending Resend Driver',
        'password_plaintext' => null,
    ]);
    $acceptedSubuser = api_fixtures()->createSubuser(['name' => 'Accepted Resend Driver']);
    api_fixtures()->grantSubuser(
        (int)$pendingSubuser['id'],
        (int)$targetCustomer['customer_number'],
        ['VEHICLES_LIST']
    );
    api_fixtures()->grantSubuser(
        (int)$acceptedSubuser['id'],
        (int)$targetCustomer['customer_number'],
        ['VEHICLES_LIST']
    );

    $pending = api_client()->post(
        '/superuser/users/' . $targetCustomer['id'] . '/subusers/' . $pendingSubuser['id'] . '/invite/resend',
        [],
        $session['headers']
    );
    $accepted = api_client()->post(
        '/superuser/users/' . $targetCustomer['id'] . '/subusers/' . $acceptedSubuser['id'] . '/invite/resend',
        [],
        $session['headers']
    );

    $pending
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();
    $accepted
        ->assertStatus(409)
        ->assertEnvelope()
        ->assertSuccess(false);

    $token = $pending->data()['invite']['setup_token'] ?? null;
    expect($token)->toBeString();
    (new \objects\subusers_o())->invalidateSetupToken((string)$token);
});
