<?php

declare(strict_types=1);

use Builder\Core\Foundation\Definition\FieldBuilder;
use Builder\Core\Foundation\Events\ResourceRecordCreated;
use Builder\Core\Foundation\Events\ResourceRecordDeleted;
use Builder\Core\Foundation\Events\ResourceRecordUpdated;
use Builder\Core\Foundation\Registry;
use Builder\Core\Foundation\Resources\Resource;
use Builder\Core\Tests\BuilderTestUser;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema;

// ---------------------------------------------------------------------------
// Test doubles
// ---------------------------------------------------------------------------

final class EventsTestModel extends Model
{
    protected $table = 'events_test_items';

    protected $guarded = [];
}

final class EventsTestResource extends Resource
{
    public static function getSlug(): string
    {
        return 'events-test-items';
    }

    public static function getLabel(): string
    {
        return 'Events Test Items';
    }

    public static function getModel(): string
    {
        return EventsTestModel::class;
    }

    public function rules(string $action): array
    {
        return ['name' => 'required|string'];
    }

    public function fields(): FieldBuilder
    {
        return FieldBuilder::make();
    }
}

// ---------------------------------------------------------------------------
// Setup
// ---------------------------------------------------------------------------

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

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

    Schema::create('revisions', function ($table): void {
        $table->id();
        $table->string('revisable_type');
        $table->unsignedBigInteger('revisable_id');
        $table->unsignedBigInteger('user_id')->nullable();
        $table->json('revision_data');
        $table->timestamps();
    });

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

afterEach(function (): void {
    Schema::dropIfExists('events_test_items');
    Schema::dropIfExists('revisions');
    Schema::dropIfExists('users');
});

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

it('dispatches ResourceRecordCreated event on store', function (): void {
    Event::fake([ResourceRecordCreated::class]);

    $admin = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@example.com', 'password' => 'x']);

    $this->actingAs($admin)
        ->post(route('builder.resource.store', 'events-test-items'), ['name' => 'Test'])
        ->assertRedirect();

    Event::assertDispatched(ResourceRecordCreated::class, function (ResourceRecordCreated $event): bool {
        return $event->record->getAttribute('name') === 'Test';
    });
});

it('dispatches ResourceRecordUpdated event on update', function (): void {
    Event::fake([ResourceRecordUpdated::class]);

    $record = EventsTestModel::create(['name' => 'Before']);
    $admin = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@example.com', 'password' => 'x']);

    $this->actingAs($admin)
        ->put(route('builder.resource.update', ['events-test-items', $record->id]), ['name' => 'After'])
        ->assertRedirect();

    Event::assertDispatched(ResourceRecordUpdated::class, function (ResourceRecordUpdated $event): bool {
        return $event->record->getAttribute('name') === 'After';
    });
});

it('dispatches ResourceRecordDeleted event on destroy', function (): void {
    Event::fake([ResourceRecordDeleted::class]);

    $record = EventsTestModel::create(['name' => 'To Delete']);
    $admin = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@example.com', 'password' => 'x']);

    $this->actingAs($admin)
        ->delete(route('builder.resource.destroy', ['events-test-items', $record->id]))
        ->assertRedirect();

    Event::assertDispatched(ResourceRecordDeleted::class, function (ResourceRecordDeleted $event) use ($record): bool {
        return (int) $event->record->getKey() === (int) $record->id;
    });
});
