<?php

use App\Models\Application;
use App\Models\AuthenticationToken;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Token\Parser;

uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);

test('JWT sub claim uses user UUID instead of ID', function () {
    $user = User::factory()->create();
    $app = Application::factory()->create();
    
    // Store authorization code
    $code = 'test-auth-code';
    Cache::put("auth_code:$code", [
        'user_id' => $user->id,
        'client_id' => $app->id,
        'scope' => 'openid profile email',
    ], now()->addMinutes(5));
    
    $this->actingAs($user);
    
    $response = $this->post(route('auth.token'), [
        'grant_type' => 'authorization_code',
        'code' => $code,
        'redirect_uri' => $app->redirect_uri,
        'client_id' => $app->client_id,
        'client_secret' => $app->client_secret,
    ]);
    
    $response->assertStatus(200);
    $data = $response->json();
    
    // Parse the JWT ID token
    $parser = new Parser(new JoseEncoder());
    $idToken = $parser->parse($data['id_token']);
    
    // Verify that the 'sub' claim contains the user's UUID, not the ID
    expect($idToken->claims()->get('sub'))->toBe((string) $user->uuid);
    expect($idToken->claims()->get('sub'))->not->toBe((string) $user->id);
    expect(Str::isUuid($idToken->claims()->get('sub')))->toBeTrue();
});

test('userinfo endpoint sub claim uses user UUID', function () {
    $user = User::factory()->create([
        'name' => 'Test User',
        'email' => 'test@example.com',
        'preferred_username' => 'testuser',
        'avatar' => 'avatar.jpg'
    ]);
    
    $app = Application::factory()->create();
    
    // Create a valid access token
    $accessToken = 'valid-access-token';
    AuthenticationToken::create([
        'user_id' => $user->id,
        'application_id' => $app->id,
        'token' => $accessToken,
        'issued_at' => now(),
        'expires_at' => now()->addHour(),
        'ip' => '127.0.0.1',
        'user_agent' => 'Test Agent'
    ]);
    
    $response = $this->get(route('auth.userinfo'), [
        'Authorization' => 'Bearer ' . $accessToken
    ]);
    
    $response->assertStatus(200);
    $data = $response->json();
    
    // Verify that the 'sub' field contains the user's UUID, not the ID
    expect($data['sub'])->toBe((string) $user->uuid);
    expect($data['sub'])->not->toBe((string) $user->id);
    expect(Str::isUuid($data['sub']))->toBeTrue();
    
    // Verify other fields are still correct
    expect($data['email'])->toBe($user->email);
    expect($data['name'])->toBe($user->name);
    expect($data['preferred_username'])->toBe($user->preferred_username);
});

test('JWT sub claim is consistent across multiple tokens for same user', function () {
    $user = User::factory()->create();
    $app = Application::factory()->create();
    
    // Generate first token
    $code1 = 'test-auth-code-1';
    Cache::put("auth_code:$code1", [
        'user_id' => $user->id,
        'client_id' => $app->id,
        'scope' => 'openid profile email',
    ], now()->addMinutes(5));
    
    $response1 = $this->post(route('auth.token'), [
        'grant_type' => 'authorization_code',
        'code' => $code1,
        'redirect_uri' => $app->redirect_uri,
        'client_id' => $app->client_id,
        'client_secret' => $app->client_secret,
    ]);
    
    // Generate second token
    $code2 = 'test-auth-code-2';
    Cache::put("auth_code:$code2", [
        'user_id' => $user->id,
        'client_id' => $app->id,
        'scope' => 'openid profile email',
    ], now()->addMinutes(5));
    
    $response2 = $this->post(route('auth.token'), [
        'grant_type' => 'authorization_code',
        'code' => $code2,
        'redirect_uri' => $app->redirect_uri,
        'client_id' => $app->client_id,
        'client_secret' => $app->client_secret,
    ]);
    
    $parser = new Parser(new JoseEncoder());
    $idToken1 = $parser->parse($response1->json()['id_token']);
    $idToken2 = $parser->parse($response2->json()['id_token']);
    
    // Both tokens should have the same 'sub' claim (user's UUID)
    expect($idToken1->claims()->get('sub'))->toBe($idToken2->claims()->get('sub'));
    expect($idToken1->claims()->get('sub'))->toBe((string) $user->uuid);
});

test('different users have different UUID sub claims', function () {
    $user1 = User::factory()->create();
    $user2 = User::factory()->create();
    $app = Application::factory()->create();
    
    // Generate token for user 1
    $code1 = 'test-auth-code-1';
    Cache::put("auth_code:$code1", [
        'user_id' => $user1->id,
        'client_id' => $app->id,
        'scope' => 'openid profile email',
    ], now()->addMinutes(5));
    
    $response1 = $this->post(route('auth.token'), [
        'grant_type' => 'authorization_code',
        'code' => $code1,
        'redirect_uri' => $app->redirect_uri,
        'client_id' => $app->client_id,
        'client_secret' => $app->client_secret,
    ]);
    
    // Generate token for user 2
    $code2 = 'test-auth-code-2';
    Cache::put("auth_code:$code2", [
        'user_id' => $user2->id,
        'client_id' => $app->id,
        'scope' => 'openid profile email',
    ], now()->addMinutes(5));
    
    $response2 = $this->post(route('auth.token'), [
        'grant_type' => 'authorization_code',
        'code' => $code2,
        'redirect_uri' => $app->redirect_uri,
        'client_id' => $app->client_id,
        'client_secret' => $app->client_secret,
    ]);
    
    $parser = new Parser(new JoseEncoder());
    $idToken1 = $parser->parse($response1->json()['id_token']);
    $idToken2 = $parser->parse($response2->json()['id_token']);
    
    // Each user should have different 'sub' claims
    expect($idToken1->claims()->get('sub'))->not->toBe($idToken2->claims()->get('sub'));
    expect($idToken1->claims()->get('sub'))->toBe((string) $user1->uuid);
    expect($idToken2->claims()->get('sub'))->toBe((string) $user2->uuid);
});