<?php

use App\Models\AuthAuditLog;
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Queue;
use Inertia\Testing\AssertableInertia;

uses(RefreshDatabase::class);

it('logs successful login attempts', function (): void {
    $user = User::factory()->create();

    $this->post('/login', [
        'email' => $user->email,
        'password' => 'password',
    ])->assertRedirect(route('dashboard', absolute: false));

    $log = AuthAuditLog::query()->latest('id')->first();

    expect($log)->not->toBeNull()
        ->and($log->event_type)->toBe('login')
        ->and($log->status)->toBe('success')
        ->and($log->identifier)->toBe(strtolower($user->email))
        ->and($log->user_id)->toBe($user->id);
});

it('logs login validation failures', function (): void {
    config()->set('app.debug', false);

    $this->from('/login')->post('/login', [
        'email' => '',
        'password' => '',
    ])->assertRedirect('/login')
        ->assertSessionHasErrors(['email', 'password']);

    $log = AuthAuditLog::query()->latest('id')->first();

    expect($log)->not->toBeNull()
        ->and($log->event_type)->toBe('login')
        ->and($log->status)->toBe('failed')
        ->and($log->reason)->toBe('validation_failed')
        ->and($log->metadata)->toMatchArray(['fields' => ['email', 'password']]);
});

it('logs successful registration attempts', function (): void {
    Queue::fake();

    $this->post('/register', [
        'email' => 'audit-register@example.com',
    ])->assertRedirect(route('setup.password.create', absolute: false));

    $log = AuthAuditLog::query()->where('event_type', 'register')->latest('id')->first();

    expect($log)->not->toBeNull()
        ->and($log->status)->toBe('success')
        ->and($log->identifier)->toBe('audit-register@example.com')
        ->and($log->reason)->toBe('user_created');
});

it('logs forgot password attempts', function (): void {
    Notification::fake();

    $user = User::factory()->create();

    $this->post('/forgot-password', ['email' => $user->email])
        ->assertSessionHas('status');

    $log = AuthAuditLog::query()->where('event_type', 'forgot_password')->latest('id')->first();

    expect($log)->not->toBeNull()
        ->and($log->status)->toBe('success')
        ->and($log->identifier)->toBe(strtolower($user->email))
        ->and($log->user_id)->toBe($user->id);
});

it('logs successful password resets', function (): void {
    Notification::fake();

    $user = User::factory()->create();

    $this->post('/forgot-password', ['email' => $user->email]);

    $token = null;

    Notification::assertSentTo($user, ResetPassword::class, function (ResetPassword $notification) use (&$token): bool {
        $token = $notification->token;

        return true;
    });

    $this->post('/reset-password', [
        'token' => $token,
        'email' => $user->email,
        'password' => 'password',
        'password_confirmation' => 'password',
    ])->assertRedirect(route('login'));

    $log = AuthAuditLog::query()->where('event_type', 'reset_password')->latest('id')->first();

    expect($log)->not->toBeNull()
        ->and($log->status)->toBe('success')
        ->and($log->identifier)->toBe(strtolower($user->email))
        ->and($log->user_id)->toBe($user->id);
});

it('limits the auth audit moderation page to admins', function (): void {
    $admin = User::factory()->create(['role' => 'admin']);
    $manager = User::factory()->create(['role' => 'manager']);

    AuthAuditLog::query()->create([
        'event_type' => 'login',
        'identifier' => 'audit@example.com',
        'status' => 'failed',
        'reason' => 'invalid_credentials',
        'ip' => '127.0.0.1',
        'metadata' => ['via' => 'email'],
        'created_at' => now(),
    ]);

    $this->actingAs($admin)
        ->get('/moderation/auth-audit')
        ->assertOk()
        ->assertInertia(fn (AssertableInertia $page) => $page
            ->component('Admin/AuthAudit')
            ->where('logs.data.0.event_type', 'login')
            ->where('logs.data.0.reason', 'invalid_credentials')
        );

    $this->actingAs($manager)
        ->get('/moderation/auth-audit')
        ->assertForbidden();
});