<?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\Definition\MenuItem;
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;

// Resource with per-action permissions
final class ActionGatedResource extends Resource
{
    public static function getSlug(): string
    {
        return 'action-gated';
    }

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

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

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

    /** @return array<string, string> */
    public function permissions(): array
    {
        return [
            'index' => 'view-items',
            'create' => 'create-items',
            'store' => 'create-items',
            'edit' => 'edit-items',
            'update' => 'edit-items',
            'destroy' => 'delete-items',
        ];
    }
}

final class ActionGatedModel extends Model
{
    protected $table = 'action_gated_items';

    protected $guarded = [];
}

final class IndexOnlyUser 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 ['view-items'];
    }
}

final class AllPermissionsUser 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 ['view-items', 'create-items', 'edit-items', 'delete-items'];
    }
}

final class NoPermissionsUser 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 [];
    }
}

final class WildcardPermissionsUser 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->json('user_widget_preferences')->nullable();
        $table->timestamps();
    });

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

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

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

// --- Per-action permission tests ---

it('authorized user can access index', function (): void {
    $user = IndexOnlyUser::create(['name' => 'User', 'email' => 'a@test.com', 'password' => 'x']);

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

it('unauthorized user gets 403 on index', function (): void {
    $user = NoPermissionsUser::create(['name' => 'User', 'email' => 'b@test.com', 'password' => 'x']);

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

it('user with index permission cannot access create', function (): void {
    $user = IndexOnlyUser::create(['name' => 'User', 'email' => 'a@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->get(route('builder.resource.create', 'action-gated'))
        ->assertStatus(403);
});

it('user with all permissions can access create', function (): void {
    $user = AllPermissionsUser::create(['name' => 'Admin', 'email' => 'admin@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->get(route('builder.resource.create', 'action-gated'))
        ->assertStatus(200);
});

it('user with index permission cannot delete', function (): void {
    $item = ActionGatedModel::create(['name' => 'target']);
    $user = IndexOnlyUser::create(['name' => 'User', 'email' => 'a@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->delete(route('builder.resource.destroy', ['resource' => 'action-gated', 'id' => $item->id]))
        ->assertStatus(403);
});

// --- Menu item permission filtering tests ---

it('menu items with permission are hidden for users lacking it', function (): void {
    app(Registry::class)->menuItem(
        MenuItem::make('gated-section')
            ->label('Gated')
            ->children([
                MenuItem::make('visible-child')->label('Visible'),
                MenuItem::make('hidden-child')->label('Hidden')->permission('special-access'),
            ]),
    );

    // IndexOnlyUser has 'view-items' but not 'special-access'
    $user = IndexOnlyUser::create(['name' => 'User', 'email' => 'a@test.com', 'password' => 'x']);

    $response = $this->actingAs($user)
        ->get(route('builder.resource.index', 'action-gated'))
        ->assertInertia(fn (AssertableInertia $page): AssertableInertia => $page->has('navigation'));

    $navigation = $response->viewData('page')['props']['navigation'] ?? [];
    $section = collect($navigation)->firstWhere('key', 'gated-section');

    expect($section)->not->toBeNull()
        ->and($section['children'])->toHaveCount(1)
        ->and($section['children'][0]['key'])->toBe('visible-child');
});

it('superuser with wildcard permission sees all menu items', function (): void {
    app(Registry::class)->menuItem(
        MenuItem::make('super-section')
            ->label('Super')
            ->children([
                MenuItem::make('child-a')->label('A')->permission('perm-a'),
                MenuItem::make('child-b')->label('B')->permission('perm-b'),
            ]),
    );

    $user = WildcardPermissionsUser::create(['name' => 'Super', 'email' => 'super@test.com', 'password' => 'x']);

    $response = $this->actingAs($user)
        ->get(route('builder.resource.index', 'action-gated'))
        ->assertInertia(fn (AssertableInertia $page): AssertableInertia => $page->has('navigation'));

    $navigation = $response->viewData('page')['props']['navigation'] ?? [];
    $section = collect($navigation)->firstWhere('key', 'super-section');

    expect($section)->not->toBeNull()
        ->and($section['children'])->toHaveCount(2);
});
