<?php

declare(strict_types=1);

use App\Models\ImpersonationLog;
use App\Models\User;
use App\Services\ImpersonationService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;

uses(RefreshDatabase::class);

test('super admin can impersonate normal user', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $targetUser = User::factory()->normalUser()->create();
    $impersonationService = app(ImpersonationService::class);

    expect($impersonationService->canImpersonate($admin, $targetUser))->toBeTrue();
});

test('normal user cannot impersonate anyone', function (): void {
    $normalUser = User::factory()->normalUser()->create();
    $targetUser = User::factory()->normalUser()->create();
    $impersonationService = app(ImpersonationService::class);

    expect($impersonationService->canImpersonate($normalUser, $targetUser))->toBeFalse();
});

test('super admin cannot impersonate another super admin', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $targetAdmin = User::factory()->superAdmin()->create();
    $impersonationService = app(ImpersonationService::class);

    expect($impersonationService->canImpersonate($admin, $targetAdmin))->toBeFalse();
});

test('cannot impersonate self', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $impersonationService = app(ImpersonationService::class);

    expect($impersonationService->canImpersonate($admin, $admin))->toBeFalse();
});

test('cannot impersonate banned user', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $bannedUser = User::factory()->bannedUser()->create();
    $impersonationService = app(ImpersonationService::class);

    expect($impersonationService->canImpersonate($admin, $bannedUser))->toBeFalse();
});

test('cannot impersonate when already impersonating', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $targetUser = User::factory()->normalUser()->create();
    $impersonationService = app(ImpersonationService::class);

    // Simulate active impersonation
    Session::put('impersonation', true);
    Session::put('original_admin_user_id', $admin->id);
    Session::put('impersonation_start_time', now());

    expect($impersonationService->canImpersonate($admin, $targetUser))->toBeFalse();
});

test('start impersonation creates log entry', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $targetUser = User::factory()->normalUser()->create();
    $impersonationService = app(ImpersonationService::class);

    $request = Request::create('/');

    $result = $impersonationService->startImpersonation($admin, $targetUser, $request);

    expect($result)->toBeTrue();
    expect($impersonationService->isImpersonating())->toBeTrue();
    expect(Session::get('original_admin_user_id'))->toBe($admin->id);

    $this->assertDatabaseHas('impersonation_logs', [
        'admin_id' => $admin->id,
        'target_user_id' => $targetUser->id,
        'status' => 'active',
    ]);
});

test('stop impersonation ends session and updates log', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $targetUser = User::factory()->normalUser()->create();
    $impersonationService = app(ImpersonationService::class);

    $request = Request::create('/');

    // Start impersonation
    $impersonationService->startImpersonation($admin, $targetUser, $request);
    $logId = Session::get('impersonation');

    // Stop impersonation
    $result = $impersonationService->stopImpersonation($request);

    expect($result)->toBeTrue();
    expect($impersonationService->isImpersonating())->toBeFalse();
    expect(Session::get('original_admin_user_id'))->toBeNull();

    $this->assertDatabaseHas('impersonation_logs', [
        'id' => $logId,
        'admin_id' => $admin->id,
        'target_user_id' => $targetUser->id,
        'status' => 'completed',
    ]);
});

test('impersonation timeout is detected correctly', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $targetUser = User::factory()->normalUser()->create();
    $impersonationService = app(ImpersonationService::class);

    $request = Request::create('/');

    // Start impersonation
    $impersonationService->startImpersonation($admin, $targetUser, $request);

    // Simulate expired session
    Session::put('impersonation_start_time', now()->subMinutes(35));

    expect($impersonationService->isImpersonationExpired())->toBeTrue();
});

test('remaining minutes calculation works correctly', function (): void {
    $admin = User::factory()->superAdmin()->create();
    $targetUser = User::factory()->normalUser()->create();
    $impersonationService = app(ImpersonationService::class);

    $request = Request::create('/');

    // Start impersonation
    $impersonationService->startImpersonation($admin, $targetUser, $request);

    // Test with 10 minutes elapsed
    Session::put('impersonation_start_time', now()->subMinutes(10));

    expect($impersonationService->getRemainingMinutes())->toBeBetween(19, 20);
});

test('force stop all impersonations terminates active sessions', function (): void {
    // Create multiple active impersonation logs
    ImpersonationLog::factory()->count(3)->active()->create();

    $impersonationService = app(ImpersonationService::class);
    $impersonationService->forceStopAllImpersonations();

    $this->assertDatabaseMissing('impersonation_logs', [
        'status' => 'active',
    ]);

    $this->assertDatabaseCount('impersonation_logs', 3);
});
