<?php

declare(strict_types=1);

usesApiSuite();

it('logs in with valid customer credentials', function (): void {
    api_test_covers('POST /auth/login', 'happy');

    api_fixtures()->setModuleConfig('reCAPTCHA', 'enabled', 'false');
    $user = api_fixtures()->createUser([
        'display_name' => 'API Login User',
        'password_plaintext' => 'Secret123!',
    ]);

    $response = api_client()->post('/auth/login', [
        'customer_number' => $user['customer_number'],
        'password' => $user['password_plaintext'],
    ]);

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

    expect($response->data())
        ->toBeArray()
        ->toHaveKey('token')
        ->and($response->data()['token'])
        ->toBeString()
        ->toHaveLength(64);
});

it('rejects invalid login payloads and credentials', function (): void {
    api_test_covers('POST /auth/login', 'failure');

    api_fixtures()->setModuleConfig('reCAPTCHA', 'enabled', 'false');
    $user = api_fixtures()->createUser([
        'password_plaintext' => 'Secret123!',
    ]);

    $missingPassword = api_client()->post('/auth/login', [
        'customer_number' => $user['customer_number'],
    ]);

    $missingPassword
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Missing required parameters: password');

    $invalidCredentials = api_client()->post('/auth/login', [
        'customer_number' => $user['customer_number'],
        'password' => 'wrong-password',
    ]);

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

it('returns the cached auth session payload for a valid token', function (): void {
    api_test_covers('GET /auth/session', 'happy');

    $session = api_fixtures()->createUserSession(['list_departments'], [
        'display_name' => 'Session User',
    ]);

    api_fixtures()->cacheAuthSessionForUser(
        $session['user'],
        $session['token'],
        ['list_departments'],
        [
            'two_factor_enabled' => false,
        ]
    );
    api_fixtures()->setModuleConfig('economic', 'transactionDraftCustomerNumber', '445566', 'int');
    api_fixtures()->setModuleConfig('economic', 'defaultDepartmentId', '75', 'int');

    $response = api_client()->get('/auth/session', $session['headers']);

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

    expect($response->data())
        ->toBeArray()
        ->toHaveKey('customer_number', $session['user']['customer_number'])
        ->toHaveKey('two_factor_enabled', false)
        ->and($response->data()['permissions'])
        ->toContain('list_departments')
        ->and($response->data()['runtime_config']['economic']['transaction_draft_customer_number'] ?? null)
        ->toBe(445566)
        ->and($response->data()['runtime_config']['economic']['default_distribution_department_id'] ?? null)
        ->toBe(75);
});

it('includes economic runtime config for uncached auth sessions', function (): void {
    api_test_covers('GET /auth/session', 'happy');

    api_fixtures()->setModuleConfig('economic', 'transactionDraftCustomerNumber', '556677', 'int');
    api_fixtures()->setModuleConfig('economic', 'defaultDepartmentId', '65', 'int');
    $session = api_fixtures()->createUserSession(['list_departments'], [
        'display_name' => 'Fresh Session User',
    ]);
    api_fixtures()->addCustomerAttribute((int)$session['user']['id'], 'restrictSpotFree');

    $response = api_client()->get('/auth/session', $session['headers']);

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

    expect($response->data())
        ->toBeArray()
        ->toHaveKey('customer_number', $session['user']['customer_number'])
        ->and($response->data()['runtime_config']['economic']['transaction_draft_customer_number'] ?? null)
        ->toBe(556677)
        ->and($response->data()['runtime_config']['economic']['default_distribution_department_id'] ?? null)
        ->toBe(65)
        ->and($response->data()['permissions'] ?? [])
        ->toContain('has_attribute_restrictSpotFree');
});

it('rejects invalid auth session tokens', function (): void {
    api_test_covers('GET /auth/session', 'auth');

    $response = api_client()->get('/auth/session', [
        'Authorization' => 'Bearer invalid-token',
    ]);

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

it('logs out and invalidates the token for future session calls', function (): void {
    api_test_covers('GET /auth/logout', 'happy');

    $session = api_fixtures()->createUserSession(['list_departments'], [
        'display_name' => 'Logout User',
    ]);

    api_fixtures()->cacheAuthSessionForUser(
        $session['user'],
        $session['token'],
        ['list_departments']
    );

    $logoutResponse = api_client()->get('/auth/logout', $session['headers']);

    $logoutResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess()
        ->assertMessage('Logged out');

    $followUpSession = api_client()->get('/auth/session', $session['headers']);

    $followUpSession
        ->assertStatus(401)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Invalid token');
});

it('logs out and invalidates cached subuser sessions', function (): void {
    api_test_covers('GET /auth/logout', 'happy');

    $user = api_fixtures()->createUser();

    $session = api_fixtures()->createSubuserSession((int)$user['customer_number'], [], [
        'username' => 'logout-driver',
    ]);

    $warmCache = api_client()->get('/subusers/me', $session['headers']);

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

    $logoutResponse = api_client()->get('/auth/logout', $session['headers']);

    $logoutResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess()
        ->assertMessage('Logged out');

    $followUpProfile = api_client()->get('/subusers/me', $session['headers']);

    $followUpProfile
        ->assertStatus(401)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Unauthorized');
});

it('scopes passkey challenges to the requested principal type', function (): void {
    api_test_covers('POST /auth/passkey/challenge', 'auth');

    api_fixtures()->setModuleConfig('reCAPTCHA', 'enabled', 'false');
    $user = api_fixtures()->createUser([
        'display_name' => 'Passkey Customer',
    ]);
    $subuser = api_fixtures()->createSubuser([
        'username' => 'passkey-driver',
    ]);
    api_fixtures()->createPasskey([
        'user_id' => (int)$user['id'],
        'is_subuser' => false,
        'credential_id' => 'customer-passkey-credential',
    ]);
    api_fixtures()->createPasskey([
        'user_id' => (int)$subuser['id'],
        'is_subuser' => true,
        'credential_id' => 'subuser-passkey-credential',
    ]);

    $subuserChallenge = api_client()->post('/auth/passkey/challenge', [
        'principal_type' => 'subuser',
        'username' => 'passkey-driver',
    ]);
    $userChallenge = api_client()->post('/auth/passkey/challenge', [
        'principal_type' => 'user',
        'customer_number' => (int)$user['customer_number'],
    ]);

    $subuserChallenge
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();
    $userChallenge
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $subuserCredentials = $subuserChallenge->data()['publicKey']['allowCredentials'] ?? [];
    $userCredentials = $userChallenge->data()['publicKey']['allowCredentials'] ?? [];

    $subuserCredentialIds = array_column($subuserCredentials, 'id');
    $userCredentialIds = array_column($userCredentials, 'id');

    expect($subuserCredentialIds)->toContain('subuser-passkey-credential');
    expect($subuserCredentialIds)->not->toContain('customer-passkey-credential');
    expect($userCredentialIds)->toContain('customer-passkey-credential');
    expect($userCredentialIds)->not->toContain('subuser-passkey-credential');

    $subuserChallengeToken = (string)($subuserChallenge->data()['challenge_token'] ?? '');
    $userChallengeToken = (string)($userChallenge->data()['challenge_token'] ?? '');
    if ($subuserChallengeToken !== '') {
        api_fixtures()->cleanupDeleteWhere('tokens', ['token' => $subuserChallengeToken]);
    }
    if ($userChallengeToken !== '') {
        api_fixtures()->cleanupDeleteWhere('tokens', ['token' => $userChallengeToken]);
    }
});

it('rejects invalid logout tokens', function (): void {
    api_test_covers('GET /auth/logout', 'auth');

    $response = api_client()->get('/auth/logout', [
        'Authorization' => 'Bearer invalid-token',
    ]);

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