<?php

use App\Enums\RoleSlug;
use App\Models\User;
use Database\Seeders\PermissionSeeder;
use Database\Seeders\RoleSeeder;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;

uses(LazilyRefreshDatabase::class);

beforeEach(function () {
    $this->withHeaders([
        'Origin' => 'http://localhost:3000',
        'Referer' => 'http://localhost:3000/',
    ]);
});

it('issues a csrf cookie for the spa', function () {
    $this->get('/sanctum/csrf-cookie')
        ->assertNoContent()
        ->assertCookie('XSRF-TOKEN')
        ->assertCookie(config('session.cookie'));
});

it('includes cors headers for the frontend origin', function () {
    $this->getJson('/api/me')
        ->assertUnauthorized()
        ->assertHeader('Access-Control-Allow-Origin', 'http://localhost:3000')
        ->assertHeader('Access-Control-Allow-Credentials', 'true');
});

it('validates the login payload', function () {
    $this->postJson('/api/login', [])
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['email', 'password']);
});

it('rejects invalid login credentials', function () {
    User::factory()->create([
        'email' => 'admin@royaldevelopers.com',
    ]);

    $this->postJson('/api/login', [
        'email' => 'admin@royaldevelopers.com',
        'password' => 'wrong-password',
    ])
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['email']);
});

it('authenticates a user and returns the current user', function () {
    $this->seed([
        PermissionSeeder::class,
        RoleSeeder::class,
    ]);

    $user = User::factory()->admin()->create([
        'email' => 'admin@royaldevelopers.com',
        'password' => 'password',
    ]);

    $expectedPermissions = array_column(RoleSlug::Admin->permissions(), 'value');

    $login = $this->postJson('/api/login', [
        'email' => $user->email,
        'password' => 'password',
    ])
        ->assertSuccessful()
        ->assertJsonStructure([
            'data' => ['id', 'name', 'email', 'role', 'permissions'],
        ])
        ->assertJsonPath('data.id', $user->id)
        ->assertJsonPath('data.name', $user->name)
        ->assertJsonPath('data.email', $user->email)
        ->assertJsonPath('data.role', RoleSlug::Admin->code())
        ->assertJsonMissingPath('data.password')
        ->assertJsonMissingPath('data.role_id')
        ->assertJsonMissingPath('data.remember_token');

    expect($login->json('data.permissions'))->toEqualCanonicalizing($expectedPermissions);

    $this->assertAuthenticated();

    $me = $this->getJson('/api/me')
        ->assertSuccessful()
        ->assertJsonPath('data.id', $user->id)
        ->assertJsonPath('data.email', $user->email)
        ->assertJsonPath('data.role', RoleSlug::Admin->code())
        ->assertJsonMissingPath('data.password');

    expect($me->json('data.permissions'))->toEqualCanonicalizing($expectedPermissions);
});

it('requires authentication for the current user endpoint', function () {
    $this->getJson('/api/me')
        ->assertUnauthorized()
        ->assertJsonPath('message', 'Unauthenticated.');
});

it('rejects login from a non-stateful origin', function () {
    $user = User::factory()->create([
        'email' => 'admin@royaldevelopers.com',
        'password' => 'password',
    ]);

    $this->withHeaders([
        'Origin' => 'http://evil.example',
        'Referer' => 'http://evil.example/',
    ])->postJson('/api/login', [
        'email' => $user->email,
        'password' => 'password',
    ])->assertForbidden();
});

it('requires authentication for logout', function () {
    $this->postJson('/api/logout')
        ->assertUnauthorized()
        ->assertJsonPath('message', 'Unauthenticated.');
});

it('logs the user out', function () {
    $user = User::factory()->create([
        'email' => 'admin@royaldevelopers.com',
        'password' => 'password',
    ]);

    $this->postJson('/api/login', [
        'email' => $user->email,
        'password' => 'password',
    ])->assertSuccessful();

    $this->postJson('/api/logout')
        ->assertNoContent();

    $this->assertGuest();

    $this->getJson('/api/me')
        ->assertUnauthorized();
});
