<?php

declare(strict_types=1);

use Builder\Core\Foundation\Registry;
use Builder\Core\Models\Language;
use Builder\Core\Tests\BuilderTestUser;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Schema;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Spatie\Permission\PermissionRegistrar;
use Spatie\Permission\Traits\HasRoles;

final class AdminableTestUser extends Authenticatable
{
    use HasRoles;

    protected $table = 'users';

    protected $guarded = [];
}

beforeEach(function (): void {
    // Permission tables are created by the builder migration (create_permission_tables.php)
    // which runs via ConsoleTestCase::setUp() -> Artisan::call('migrate').
    // We only need to create the users table here.
    Schema::create('users', function ($table): void {
        $table->id();
        $table->string('name');
        $table->string('email')->unique();
        $table->string('password');
        $table->timestamp('activated_at')->nullable();
        $table->timestamps();
    });

    resolve(PermissionRegistrar::class)->forgetCachedPermissions();
});

it('creates the read:admin permission', function (): void {
    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'no')
        ->assertSuccessful();

    expect(Permission::query()->where('name', 'read:admin')->exists())->toBeTrue();
});

it('creates the System administrator role', function (): void {
    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'no')
        ->assertSuccessful();

    expect(Role::query()->where('name', 'System administrator')->exists())->toBeTrue();
});

it('assigns all seeded permissions to the System administrator role', function (): void {
    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'no')
        ->assertSuccessful();

    $role = Role::query()->where('name', 'System administrator')->first();

    expect($role)->not->toBeNull()
        ->and($role->hasPermissionTo('read:admin'))->toBeTrue()
        ->and($role->hasPermissionTo('read:users'))->toBeTrue()
        ->and($role->hasPermissionTo('read:roles'))->toBeTrue();
});

it('is idempotent when run multiple times', function (): void {
    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'no')
        ->assertSuccessful();
    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'no')
        ->assertSuccessful();

    expect(Permission::query()->where('name', 'read:admin')->count())->toBe(1)
        ->and(Role::query()->where('name', 'System administrator')->count())->toBe(1);
});

it('creates an administrator user with the System administrator role when model has HasRoles', function (): void {
    config(['auth.providers.users.model' => AdminableTestUser::class]);

    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'yes')
        ->expectsQuestion('Name', 'Admin User')
        ->expectsQuestion('Email address', 'admin@example.com')
        ->expectsQuestion('Password', 'secret')
        ->assertSuccessful();

    $user = AdminableTestUser::query()->where('email', 'admin@example.com')->first();

    expect($user)->not->toBeNull()
        ->and($user->hasRole('System administrator'))->toBeTrue();
});

it('creates a user without assigning a role when model lacks HasRoles', function (): void {
    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'yes')
        ->expectsQuestion('Name', 'Plain User')
        ->expectsQuestion('Email address', 'plain@example.com')
        ->expectsQuestion('Password', 'secret')
        ->assertSuccessful();

    expect(BuilderTestUser::query()->where('email', 'plain@example.com')->exists())->toBeTrue();
});

it('seeds resource permissions from registered resources', function (): void {
    // UserResource and RoleResource are registered by CoreExtensionsServiceProvider
    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'no')
        ->assertSuccessful();

    $expectedPermissions = [
        'read:users', 'create:users', 'update:users', 'delete:users',
        'read:roles', 'create:roles', 'update:roles', 'delete:roles',
        'read:settings', 'manage:settings',
    ];

    foreach ($expectedPermissions as $name) {
        expect(Permission::query()->where('name', $name)->exists())
            ->toBeTrue("Permission '{$name}' was not seeded");
    }
});

it('seeds permissions registered directly on the registry', function (): void {
    app(Registry::class)->permissions('view:analytics', 'export:reports');

    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'no')
        ->assertSuccessful();

    expect(Permission::query()->where('name', 'view:analytics')->exists())->toBeTrue()
        ->and(Permission::query()->where('name', 'export:reports')->exists())->toBeTrue();
});

it('seeds languages from config when none exist', function (): void {
    Language::query()->delete();
    config(['builder.locales' => ['en' => 'English', 'nl' => 'Nederlands']]);

    $this->artisan('builder:install')
        ->expectsConfirmation('Would you like to create an administrator user now?', 'no')
        ->assertSuccessful();

    expect(Language::query()->count())->toBe(2)
        ->and(Language::query()->where('code', 'en')->where('is_default', true)->exists())->toBeTrue()
        ->and(Language::query()->where('code', 'nl')->where('is_default', false)->exists())->toBeTrue();
});
