<?php

declare(strict_types=1);

use App\Models\LoginHistory;
use App\Models\TrustedDevice;
use App\Models\User;
use Database\Seeders\RoleAndPermissionSeeder;

beforeEach(function (): void {
    $this->seed(RoleAndPermissionSeeder::class);
    $this->accountUrl = 'http://'.config('app.domains.account');
    $this->adminUrl = 'http://'.config('app.domains.admin');

    $this->user = User::factory()->customer()->create([
        'password' => bcrypt('password'),
    ]);
});

it('creates a trusted device after 2FA with trust_device flag', function (): void {
    // Enable 2FA for user
    $this->user->forceFill([
        'two_factor_secret' => encrypt('test-secret'),
        'two_factor_confirmed_at' => now(),
    ])->save();

    $listener = new \App\Listeners\HandleTwoFactorAuthenticated;

    // Simulate a request with trust_device
    $request = \Illuminate\Http\Request::create('/two-factor-challenge', 'POST', [
        'trust_device' => true,
    ]);
    $request->setUserResolver(fn () => $this->user);

    // Set the request in the container
    app()->instance('request', $request);

    $event = new \Laravel\Fortify\Events\ValidTwoFactorAuthenticationCodeProvided($this->user);
    $listener->handle($event);

    expect(TrustedDevice::query()->where('user_id', $this->user->id)->count())->toBe(1);

    $device = TrustedDevice::query()->where('user_id', $this->user->id)->first();
    expect($device->expires_at->isFuture())->toBeTrue();
    expect($device->device_name)->not->toBeNull();
});

it('does not create a trusted device when trust_device flag is false', function (): void {
    $listener = new \App\Listeners\HandleTwoFactorAuthenticated;

    $request = \Illuminate\Http\Request::create('/two-factor-challenge', 'POST', [
        'trust_device' => false,
    ]);
    app()->instance('request', $request);

    $event = new \Laravel\Fortify\Events\ValidTwoFactorAuthenticationCodeProvided($this->user);
    $listener->handle($event);

    expect(TrustedDevice::query()->where('user_id', $this->user->id)->count())->toBe(0);
});

it('trusted device is recognized as active and not expired', function (): void {
    $device = TrustedDevice::factory()->create([
        'user_id' => $this->user->id,
        'expires_at' => now()->addDays(30),
    ]);

    expect($device->isExpired())->toBeFalse();
    expect(TrustedDevice::query()->active()->where('user_id', $this->user->id)->count())->toBe(1);
});

it('expired device is not recognized as active', function (): void {
    $device = TrustedDevice::factory()->expired()->create([
        'user_id' => $this->user->id,
    ]);

    expect($device->isExpired())->toBeTrue();
    expect(TrustedDevice::query()->active()->where('user_id', $this->user->id)->count())->toBe(0);
});

it('customer removes individual trusted device', function (): void {
    $device = TrustedDevice::factory()->create([
        'user_id' => $this->user->id,
    ]);

    $response = $this->actingAs($this->user)
        ->delete("{$this->accountUrl}/profile/trusted-devices/{$device->id}");

    $response->assertRedirect();
    expect(TrustedDevice::query()->find($device->id))->toBeNull();
});

it('customer removes all trusted devices', function (): void {
    TrustedDevice::factory()->count(3)->create([
        'user_id' => $this->user->id,
    ]);

    expect(TrustedDevice::query()->where('user_id', $this->user->id)->count())->toBe(3);

    $response = $this->actingAs($this->user)
        ->delete("{$this->accountUrl}/profile/trusted-devices");

    $response->assertRedirect();
    expect(TrustedDevice::query()->where('user_id', $this->user->id)->count())->toBe(0);
});

it('customer cannot remove another user\'s trusted device', function (): void {
    $otherUser = User::factory()->customer()->create();

    $device = TrustedDevice::factory()->create([
        'user_id' => $otherUser->id,
    ]);

    $response = $this->actingAs($this->user)
        ->delete("{$this->accountUrl}/profile/trusted-devices/{$device->id}");

    $response->assertForbidden();
    expect(TrustedDevice::query()->find($device->id))->not->toBeNull();
});

it('customer logs out other sessions with correct password', function (): void {
    $response = $this->actingAs($this->user)
        ->delete("{$this->accountUrl}/profile/sessions", [
            'password' => 'password',
        ]);

    $response->assertRedirect();
    $response->assertSessionHas('success');
});

it('customer cannot log out other sessions with wrong password', function (): void {
    $response = $this->actingAs($this->user)
        ->delete("{$this->accountUrl}/profile/sessions", [
            'password' => 'wrong-password',
        ]);

    $response->assertRedirect();
    $response->assertSessionHasErrors('password');
});

it('admin force-logouts customer', function (): void {
    $admin = User::factory()->admin()->create();

    // Create trusted devices for target user
    TrustedDevice::factory()->count(2)->create([
        'user_id' => $this->user->id,
    ]);

    $response = $this->actingAs($admin)
        ->post("{$this->adminUrl}/customers/{$this->user->id}/force-logout");

    $response->assertRedirect();
    $response->assertSessionHas('success');

    // Verify trusted devices were cleared
    expect(TrustedDevice::query()->where('user_id', $this->user->id)->count())->toBe(0);

    // Verify remember token was cycled
    $this->user->refresh();
    expect($this->user->remember_token)->not->toBeNull();
});

it('checks that trusted device lookup works for active devices', function (): void {
    $ip = '192.168.1.100';
    $ua = 'Mozilla/5.0 Test Browser';
    $deviceHash = LoginHistory::generateDeviceHash($ua, $ip);

    // Enable 2FA for user
    $this->user->forceFill([
        'two_factor_secret' => encrypt('test-secret'),
        'two_factor_confirmed_at' => now(),
    ])->save();

    // Create an active trusted device
    TrustedDevice::factory()->create([
        'user_id' => $this->user->id,
        'device_hash' => $deviceHash,
        'expires_at' => now()->addDays(30),
    ]);

    // Verify the trusted device exists and is active
    $trustedDevice = TrustedDevice::query()
        ->where('user_id', $this->user->id)
        ->where('device_hash', $deviceHash)
        ->active()
        ->first();

    expect($trustedDevice)->not->toBeNull();
    expect($trustedDevice->isExpired())->toBeFalse();
});

it('expired trusted device does not match active scope', function (): void {
    $ip = '192.168.1.100';
    $ua = 'Mozilla/5.0 Test Browser';
    $deviceHash = LoginHistory::generateDeviceHash($ua, $ip);

    TrustedDevice::factory()->expired()->create([
        'user_id' => $this->user->id,
        'device_hash' => $deviceHash,
    ]);

    $trustedDevice = TrustedDevice::query()
        ->where('user_id', $this->user->id)
        ->where('device_hash', $deviceHash)
        ->active()
        ->first();

    expect($trustedDevice)->toBeNull();
});
