<?php

declare(strict_types=1);

use Builder\Core\Foundation\Concerns\HasBuilderAccess;
use Builder\Core\Foundation\Contracts\BuilderAccessible;
use Builder\Core\Foundation\Definition\Field;
use Builder\Core\Foundation\Definition\FieldBuilder;
use Builder\Core\Foundation\Registry;
use Builder\Core\Foundation\Resources\Resource;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Schema;
use Inertia\Testing\AssertableInertia;

// A resource requiring a specific permission
final class ProtectedResource extends Resource
{
    public static function getSlug(): string
    {
        return 'protected-items';
    }

    public static function getLabel(): string
    {
        return 'Protected';
    }

    /** @return class-string<Model> */
    public static function getModel(): string
    {
        return ProtectedModel::class;
    }

    public function getPermission(): ?string
    {
        return 'manage-protected';
    }

    public function fields(): FieldBuilder
    {
        return FieldBuilder::make()->add(Field::text('name'));
    }
}

final class ProtectedModel extends Model
{
    protected $table = 'protected_items';

    protected $guarded = [];
}

// A user WITH the required permission
final class PermittedUser extends Authenticatable implements BuilderAccessible
{
    use HasBuilderAccess;

    protected $table = 'users';

    protected $guarded = [];

    public function canAccessBuilder(): bool
    {
        return true;
    }

    /** @return list<string> */
    public function builderPermissions(): array
    {
        return ['manage-protected'];
    }
}

// A user WITHOUT the required permission
final class UnpermittedUser extends Authenticatable implements BuilderAccessible
{
    use HasBuilderAccess;

    protected $table = 'users';

    protected $guarded = [];

    public function canAccessBuilder(): bool
    {
        return true;
    }

    /** @return list<string> */
    public function builderPermissions(): array
    {
        return ['some-other-permission'];
    }
}

// A superuser with wildcard permissions
final class SuperUser extends Authenticatable implements BuilderAccessible
{
    use HasBuilderAccess;

    protected $table = 'users';

    protected $guarded = [];

    public function canAccessBuilder(): bool
    {
        return true;
    }

    /** @return list<string> */
    public function builderPermissions(): array
    {
        return ['*'];
    }
}

beforeEach(function (): void {
    Schema::create('users', function ($table): void {
        $table->id();
        $table->string('name')->nullable();
        $table->string('email')->nullable();
        $table->string('password')->nullable();
        $table->timestamps();
    });

    Schema::create('protected_items', function ($table): void {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });

    config(['auth.providers.users.model' => PermittedUser::class]);

    app(Registry::class)->resource(ProtectedResource::class);
});

it('allows user with the required permission', function (): void {
    $user = PermittedUser::create(['name' => 'Admin', 'email' => 'a@a.com', 'password' => 'x']);

    $this->actingAs($user)
        ->get(route('builder.resource.index', 'protected-items'))
        ->assertStatus(200);
});

it('allows superuser with wildcard permission', function (): void {
    $user = SuperUser::create(['name' => 'Super', 'email' => 'super@a.com', 'password' => 'x']);

    $this->actingAs($user)
        ->get(route('builder.resource.index', 'protected-items'))
        ->assertStatus(200);
});

it('denies user without required permission', function (): void {
    $user = UnpermittedUser::create(['name' => 'Plain', 'email' => 'plain@a.com', 'password' => 'x']);

    $this->actingAs($user)
        ->get(route('builder.resource.index', 'protected-items'))
        ->assertStatus(403);
});

it('shares permissions in Inertia shared data', function (): void {
    $user = PermittedUser::create(['name' => 'Admin', 'email' => 'a@a.com', 'password' => 'x']);

    $this->actingAs($user)
        ->get(route('builder.resource.index', 'protected-items'))
        ->assertInertia(fn (AssertableInertia $page): AssertableInertia => $page
            ->where('auth.permissions', ['manage-protected']),
        );
});

it('resource definition exposes permission', function (): void {
    $def = app(Registry::class)->resolveBySlug('protected-items');

    expect($def)->not->toBeNull()
        ->and($def->getPermission())->toBe('manage-protected')
        ->and($def->toArray()['permission'])->toBe('manage-protected');
});
