<?php

declare(strict_types=1);

use Builder\Core\Foundation\Definition\Field;
use Builder\Core\Foundation\Definition\FieldBuilder;
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\Schema;
use Inertia\Testing\AssertableInertia;

final class RepeaterTestModel extends Model
{
    protected $table = 'repeater_test_items';

    protected $guarded = [];

    /** @return array<string, string> */
    protected function casts(): array
    {
        return ['items' => 'array'];
    }
}

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

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

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

    public function fields(): FieldBuilder
    {
        return FieldBuilder::make()->add(
            Field::repeater('items')
                ->label('Items')
                ->schema([
                    Field::text('title')->label('Title')->rules(['required', 'string', 'max:255']),
                    Field::textarea('body')->label('Body')->rules(['nullable', 'string']),
                ])
                ->maxRows(5)
                ->rules(['nullable', 'array']),
        );
    }

    /** @return array<string, mixed> */
    public function rules(string $action): array
    {
        return [
            'items' => ['nullable', 'array', 'max:5'],
            'items.*.title' => ['required', 'string', 'max:255'],
            'items.*.body' => ['nullable', 'string'],
        ];
    }
}

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('repeater_test_items', function ($table): void {
        $table->id();
        $table->json('items')->nullable();
        $table->timestamps();
    });

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

it('create form renders with RepeaterInput component and correct schema', function (): void {
    $user = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->get(route('builder.resource.create', 'repeater-test-items'))
        ->assertInertia(fn (AssertableInertia $page): AssertableInertia => $page
            ->component('Resource/Form')
            ->where('resource.sections.0.fields.0.component', 'RepeaterInput')
            ->where('resource.sections.0.fields.0.maxRows', 5)
            ->where('resource.sections.0.fields.0.fields.0.name', 'title')
            ->where('resource.sections.0.fields.0.fields.1.name', 'body'),
        );
});

it('stores a JSON array to the database', function (): void {
    $user = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->post(route('builder.resource.store', 'repeater-test-items'), [
            'items' => [
                ['title' => 'First', 'body' => 'Hello'],
                ['title' => 'Second', 'body' => null],
            ],
        ])
        ->assertRedirect();

    $record = RepeaterTestModel::first();
    expect($record->items)->toHaveCount(2)
        ->and($record->items[0]['title'])->toBe('First')
        ->and($record->items[1]['title'])->toBe('Second');
});

it('stores empty repeater as null', function (): void {
    $user = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->post(route('builder.resource.store', 'repeater-test-items'), [
            'items' => null,
        ])
        ->assertRedirect();

    expect(RepeaterTestModel::first()->items)->toBeNull();
});

it('validation rejects missing required sub-field', function (): void {
    $user = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->post(route('builder.resource.store', 'repeater-test-items'), [
            'items' => [
                ['title' => '', 'body' => null],
            ],
        ])
        ->assertSessionHasErrors('items.0.title');
});

it('validation rejects more rows than maxRows', function (): void {
    $user = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@test.com', 'password' => 'x']);

    $rows = array_fill(0, 6, ['title' => 'Row', 'body' => null]);

    $this->actingAs($user)
        ->post(route('builder.resource.store', 'repeater-test-items'), [
            'items' => $rows,
        ])
        ->assertSessionHasErrors('items');
});

it('update replaces repeater data', function (): void {
    $record = RepeaterTestModel::create([
        'items' => [['title' => 'Old', 'body' => null]],
    ]);
    $user = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->put(route('builder.resource.update', ['resource' => 'repeater-test-items', 'id' => $record->id]), [
            'items' => [
                ['title' => 'Updated', 'body' => 'New body'],
            ],
        ])
        ->assertRedirect();

    $record->refresh();
    expect($record->items)->toHaveCount(1)
        ->and($record->items[0]['title'])->toBe('Updated');
});

it('edit form receives existing repeater data', function (): void {
    $record = RepeaterTestModel::create([
        'items' => [['title' => 'Existing', 'body' => 'Desc']],
    ]);
    $user = BuilderTestUser::create(['name' => 'Admin', 'email' => 'admin@test.com', 'password' => 'x']);

    $this->actingAs($user)
        ->get(route('builder.resource.edit', ['resource' => 'repeater-test-items', 'id' => $record->id]))
        ->assertInertia(fn (AssertableInertia $page): AssertableInertia => $page
            ->component('Resource/Form')
            ->where('record.items.0.title', 'Existing')
            ->where('record.items.0.body', 'Desc'),
        );
});
