<?php

use App\Enums\IsActive;
use App\Enums\RoleEnum;
use App\Filament\Resources\Master\Users\Pages\ManageUsers;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->user = User::factory()->create(['is_active' => IsActive::ACTIVE]);

    // Setup Permissions
    $permissions = [
        'ViewAny:User',
        'Create:User',
        'Update:User',
        'Delete:User',
        'Restore:User',
        'ForceDelete:User',
    ];

    foreach ($permissions as $permission) {
        Permission::firstOrCreate(['name' => $permission, 'guard_name' => 'web']);
    }

    $this->user->givePermissionTo($permissions);

    // Create a default role for testing
    Role::firstOrCreate(['name' => RoleEnum::ADMINISTRATOR->value, 'guard_name' => 'web']);
});

test('can render user list page', function () {
    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->assertSuccessful();
});

test('can list users', function () {
    $users = User::factory()->count(5)->create();

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->call('loadTable')
        ->assertCanSeeTableRecords($users);
});

test('cannot see developers in the list', function () {
    $devRole = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']);
    $developer = User::factory()->create();
    $developer->assignRole($devRole);

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->call('loadTable')
        ->assertCanNotSeeTableRecords([$developer]);
});

test('can create user', function () {
    $role = Role::where('name', RoleEnum::ADMINISTRATOR->value)->first();

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->mountAction('create')
        ->setActionData([
            'name' => 'Test User',
            'email' => 'test@example.com',
            'username' => 'testuser',
            'roles' => [$role->id],
        ])
        ->callMountedAction()
        ->assertHasNoActionErrors();

    $this->assertDatabaseHas('users', [
        'name' => 'Test User',
        'email' => 'test@example.com',
        'username' => 'testuser',
        'is_active' => IsActive::ACTIVE->value,
    ]);

    $newUser = User::where('email', 'test@example.com')->first();
    expect($newUser->roles->pluck('id'))->toContain($role->id);
});

test('validation: required fields', function () {
    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->mountAction('create')
        ->setActionData([
            'name' => '',
            'email' => '',
            'username' => '',
            'roles' => [],
        ])
        ->callMountedAction()
        ->assertHasActionErrors([
            'name' => 'required',
            'email' => 'required',
            'username' => 'required',
            'roles' => 'required',
        ]);
});

test('validation: username length and format', function () {
    $this->actingAs($this->user);

    // Too short
    Livewire::test(ManageUsers::class)
        ->mountAction('create')
        ->setActionData(['username' => 'abc'])
        ->callMountedAction()
        ->assertHasActionErrors(['username' => 'min']);

    // Invalid format
    Livewire::test(ManageUsers::class)
        ->mountAction('create')
        ->setActionData(['username' => 'user name'])
        ->callMountedAction()
        ->assertHasActionErrors(['username' => 'regex']);
});

test('can edit user', function () {
    $userToEdit = User::factory()->create();

    $this->actingAs($this->user);

    $role = Role::where('name', RoleEnum::ADMINISTRATOR->value)->first();

    Livewire::test(ManageUsers::class)
        ->mountTableAction('edit', $userToEdit)
        ->setActionData([
            'name' => 'Updated Name',
            'roles' => [$role->id],
        ])
        ->callMountedTableAction()
        ->assertHasNoActionErrors();

    expect($userToEdit->refresh()->name)->toBe('Updated Name');
});

test('can change user password', function () {
    $userToModify = User::factory()->create();
    $oldPassword = $userToModify->password;

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->mountTableAction('changePassword', $userToModify)
        ->setActionData([
            'password' => 'newpassword123',
        ])
        ->callMountedTableAction()
        ->assertHasNoActionErrors();

    $userToModify->refresh();
    expect(Hash::check('newpassword123', $userToModify->password))->toBeTrue();
    expect($userToModify->password)->not->toBe($oldPassword);
});

test('can toggle user active status', function () {
    $userToToggle = User::factory()->create(['is_active' => IsActive::INACTIVE]);

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->call('updateTableColumnState', 'is_active', $userToToggle->getKey(), true);

    expect($userToToggle->refresh()->is_active)->toBe(IsActive::ACTIVE);
});

test('cannot toggle active status for company role', function () {
    $companyRole = Role::firstOrCreate(['name' => RoleEnum::PERUSAHAAN->value, 'guard_name' => 'web']);
    $companyUser = User::factory()->create(['is_active' => IsActive::ACTIVE]);
    $companyUser->assignRole($companyRole);

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->call('updateTableColumnState', 'is_active', $companyUser->getKey(), false);

    expect($companyUser->refresh()->is_active)->toBe(IsActive::ACTIVE);
});

test('unauthorized user cannot render user page', function () {
    $unauthorizedUser = User::factory()->create(['is_active' => IsActive::ACTIVE]);

    $this->actingAs($unauthorizedUser);

    Livewire::test(ManageUsers::class)
        ->assertForbidden();
});

test('can delete user', function () {
    $userToDelete = User::factory()->create();

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->callTableAction('delete', $userToDelete);

    $this->assertSoftDeleted($userToDelete);
});

test('can restore deleted user', function () {
    $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']);
    $this->user->assignRole($roleDev);

    $userToRestore = User::factory()->create(['deleted_at' => now()]);

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->filterTable('trashed', 'with')
        ->callTableAction('restore', $userToRestore);

    $this->assertDatabaseHas('users', [
        'id' => $userToRestore->id,
        'deleted_at' => null,
    ]);
});

test('can force delete user', function () {
    $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']);
    $this->user->assignRole($roleDev);

    $userToForceDelete = User::factory()->create(['deleted_at' => now()]);

    $this->actingAs($this->user);

    Livewire::test(ManageUsers::class)
        ->filterTable('trashed', 'only')
        ->callTableAction('forceDelete', $userToForceDelete);

    $this->assertDatabaseMissing('users', ['id' => $userToForceDelete->id]);
});
