<?php

use App\Enums\UserRole;
use App\Filament\Resources\Users\Pages\EditUser;
use App\Filament\Resources\Users\Pages\ListUsers;
use App\Filament\Resources\Users\Pages\ViewUser;
use App\Models\User;
use Filament\Facades\Filament;
use Filament\Forms\Components\Select;
use Filament\Infolists\Components\TextEntry;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TernaryFilter;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

beforeEach(function () {
    app()->setLocale('fr');

    Filament::setCurrentPanel(Filament::getPanel('dashboard'));
});

it('suspends and reactivates users from the filament users table', function () {
    $admin = User::factory()->create([
        'role' => UserRole::ADMIN,
    ]);
    $user = User::factory()->create();

    $this->actingAs($admin);

    Livewire::test(ListUsers::class)
        ->assertTableColumnExists(
            'suspended_at',
            fn (TextColumn $column): bool => $column->getLabel() === 'Suspendu le',
        )
        ->assertTableFilterExists(
            'suspended_at',
            fn (TernaryFilter $filter): bool => $filter->getLabel() === 'Suspendu',
        )
        ->callTableAction('suspend', $user);

    expect($user->fresh()->suspended_at)->not->toBeNull()
        ->and($user->fresh()->suspended_by)->toBe($admin->id)
        ->and($user->fresh()->suspended_reason)->toBe('Suspendre');

    Livewire::test(ListUsers::class)
        ->callTableAction('unsuspend', $user->fresh());

    expect($user->fresh()->suspended_at)->toBeNull()
        ->and($user->fresh()->suspended_by)->toBeNull()
        ->and($user->fresh()->suspended_reason)->toBeNull();
});

it('exposes translated user roles in filament user screens', function () {
    $admin = User::factory()->create([
        'role' => UserRole::ADMIN,
    ]);
    $user = User::factory()->create([
        'role' => UserRole::MODERATOR,
    ]);

    $this->actingAs($admin);

    $expectedOptions = [
        'user' => 'Utilisateur',
        'admin' => 'Administrateur',
        'moderator' => 'Modérateur',
    ];

    Livewire::test(EditUser::class, ['record' => $user->getKey()])
        ->assertFormFieldExists(
            'role',
            null,
            fn (Select $field): bool => $field->getLabel() === 'Rôle'
                && $field->getOptions() === $expectedOptions,
        );

    Livewire::test(ViewUser::class, ['record' => $user->getKey()])
        ->assertSchemaComponentExists(
            'role',
            'infolist',
            fn (TextEntry $entry): bool => $entry->getLabel() === 'Rôle',
        );

    Livewire::test(ListUsers::class)
        ->assertTableColumnExists(
            'role',
            fn (TextColumn $column): bool => $column->getLabel() === 'Rôle',
        )
        ->assertTableFilterExists(
            'role',
            fn (SelectFilter $filter): bool => $filter->getLabel() === 'Rôle'
                && $filter->getOptions() === $expectedOptions,
        );
});

it('changes a user role by id with the artisan command', function () {
    $user = User::factory()->create([
        'role' => UserRole::USER,
    ]);

    $this->artisan('user:change-role', [
        'userId' => $user->getKey(),
        'role' => UserRole::ADMIN->value,
    ])
        ->expectsOutput("User {$user->getKey()} role changed to admin.")
        ->assertExitCode(0);

    expect($user->fresh()->role)->toBe(UserRole::ADMIN);
});

it('rejects an invalid user role', function () {
    $user = User::factory()->create([
        'role' => UserRole::USER,
    ]);

    $this->artisan('user:change-role', [
        'userId' => $user->getKey(),
        'role' => 'owner',
    ])
        ->expectsOutput('Invalid role. Available roles: user, admin, moderator.')
        ->assertExitCode(1);

    expect($user->fresh()->role)->toBe(UserRole::USER);
});

it('rejects an unknown user id', function () {
    $this->artisan('user:change-role', [
        'userId' => '01J00000000000000000000000',
        'role' => UserRole::ADMIN->value,
    ])
        ->expectsOutput('User not found.')
        ->assertExitCode(1);
});
