<?php

declare(strict_types=1);

use Builder\Core\Extensions\Settings\Pages\GeneralSettings;
use Builder\Core\Foundation\Account\AccountExtension;
use Builder\Core\Foundation\Account\AccountTab;
use Builder\Core\Foundation\Auth\AuthenticationProvider;
use Builder\Core\Foundation\Definition\FieldBuilder;
use Builder\Core\Foundation\Definition\MenuItem;
use Builder\Core\Foundation\Registry;
use Builder\Core\Foundation\Resources\Extension;
use Builder\Core\Foundation\Resources\Resource;
use Builder\Core\Foundation\Resources\ResourceDefinition;
use Builder\Core\Foundation\Settings\SettingsExtension;
use Builder\Core\Foundation\Settings\SettingsSection;
use Illuminate\Database\Eloquent\Model;

final class RegResource extends Resource
{
    public static function getSlug(): string
    {
        return 'posts';
    }

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

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

final class RegExtension extends Extension
{
    /** @return class-string<resource> */
    public static function getExtends(): string
    {
        return RegResource::class;
    }
}

it('resource registers a resource by slug', function (): void {
    $registry = new Registry;
    $registry->resource(RegResource::class);

    expect($registry->resources())->toHaveKey('posts');
});

it('resolveBySlug returns null for unknown slug', function (): void {
    $registry = new Registry;

    expect($registry->resolveBySlug('nonexistent'))->toBeNull();
});

it('resolveBySlug returns ResourceDefinition for known slug', function (): void {
    $registry = new Registry;
    $registry->resource(RegResource::class);

    expect($registry->resolveBySlug('posts'))->toBeInstanceOf(ResourceDefinition::class);
});

it('extend registers an extension', function (): void {
    $registry = new Registry;
    $registry->resource(RegResource::class);
    $registry->extend(RegExtension::class);

    $def = $registry->resolveBySlug('posts');

    expect($def)->not->toBeNull();
});

it('resolveResource resolves resource directly by class', function (): void {
    $registry = new Registry;

    expect($registry->resolveResource(RegResource::class))->toBeInstanceOf(ResourceDefinition::class);
});

it('menuItem adds a menu item', function (): void {
    $registry = new Registry;
    $registry->menuItem(MenuItem::make('pages')->priority(5));
    $registry->menuItem(MenuItem::make('media')->priority(1));

    $items = $registry->menuItems();

    expect($items)->toHaveCount(2)
        ->and($items[0]->getKey())->toBe('media')  // sorted by priority
        ->and($items[1]->getKey())->toBe('pages');
});

it('menuItems returns sorted by priority', function (): void {
    $registry = new Registry;
    $registry->menuItem(MenuItem::make('c')->priority(30));
    $registry->menuItem(MenuItem::make('a')->priority(10));
    $registry->menuItem(MenuItem::make('b')->priority(20));

    $items = $registry->menuItems();

    expect(array_map(fn (MenuItem $i): string => $i->getKey(), $items))->toBe(['a', 'b', 'c']);
});

// ── AccountTab ────────────────────────────────────────────────────────────────

it('accountTab registers tabs and accountTabs returns them', function (): void {
    $registry = new Registry;
    $tab = AccountTab::make('custom', 'CustomTabComponent');
    $registry->accountTab($tab);

    expect($registry->accountTabs())->toHaveCount(1)
        ->and($registry->accountTabs()[0]->getKey())->toBe('custom');
});

it('accountTabs sorts by order', function (): void {
    $registry = new Registry;
    $registry->accountTab(AccountTab::make('b', 'B')->order(20));
    $registry->accountTab(AccountTab::make('a', 'A')->order(10));

    $tabs = $registry->accountTabs();

    expect($tabs[0]->getKey())->toBe('a')
        ->and($tabs[1]->getKey())->toBe('b');
});

// ── AccountExtension ──────────────────────────────────────────────────────────

it('accountExtension registers instances', function (): void {
    $ext = new class extends AccountExtension {};
    $registry = new Registry;
    $registry->accountExtension($ext);

    expect($registry->accountExtensions())->toContain($ext);
});

// ── SettingsSection ───────────────────────────────────────────────────────────

it('settingsSection registers sections', function (): void {
    $registry = new Registry;
    $section = SettingsSection::make('extra');
    $registry->settingsSection($section);

    expect($registry->settingsSections())->toHaveCount(1)
        ->and($registry->settingsSections()[0]->getKey())->toBe('extra');
});

it('settingsSections sorts by order', function (): void {
    $registry = new Registry;
    $registry->settingsSection(SettingsSection::make('b')->order(20));
    $registry->settingsSection(SettingsSection::make('a')->order(10));

    $sections = $registry->settingsSections();

    expect($sections[0]->getKey())->toBe('a')
        ->and($sections[1]->getKey())->toBe('b');
});

// ── translationNamespace ──────────────────────────────────────────────────────

it('translationNamespace registers and returns namespaces', function (): void {
    $registry = new Registry;
    $registry->translationNamespace('media', 'media::media');

    expect($registry->translationNamespaces())->toBe(['media' => 'media::media']);
});

// ── resource with menuPriority ────────────────────────────────────────────────

final class MenuPriorityResource extends Resource
{
    public static function getSlug(): string
    {
        return 'menu-items';
    }

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

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

    public function getMenuPriority(): ?int
    {
        return 5;
    }
}

it('resource with non-null menuPriority auto-registers a menu item', function (): void {
    $registry = new Registry;
    $registry->resource(MenuPriorityResource::class);

    $items = $registry->menuItems();

    expect($items)->toHaveCount(1)
        ->and($items[0]->getKey())->toBe('resource-menu-items');
});

// ── extendSettings ────────────────────────────────────────────────────────────

final class RegSettingsExtension extends SettingsExtension
{
    public static function getExtends(): string
    {
        return GeneralSettings::class;
    }

    public static function getSettingsClass(): string
    {
        return GeneralSettings::class;
    }

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

    public function rules(): array
    {
        return [];
    }
}

/** Extension that does NOT override sections() or rules() — exercises parent defaults. */
final class MinimalSettingsExtension extends SettingsExtension
{
    public static function getExtends(): string
    {
        return GeneralSettings::class;
    }

    public static function getSettingsClass(): string
    {
        return GeneralSettings::class;
    }
}

it('extendSettings registers an extension keyed by the target class', function (): void {
    $registry = new Registry;
    $result = $registry->extendSettings(RegSettingsExtension::class);

    // Verify chaining and state via reflection
    $prop = (new ReflectionClass($registry))->getProperty('settingsExtensions');
    $prop->setAccessible(true);
    $extensions = $prop->getValue($registry);

    expect($result)->toBe($registry)
        ->and($extensions)->toHaveKey(GeneralSettings::class)
        ->and($extensions[GeneralSettings::class])->toContain(RegSettingsExtension::class);
});

it('SettingsExtension fields and rules default to empty', function (): void {
    $ext = new RegSettingsExtension;

    expect($ext->fields())->toBeInstanceOf(FieldBuilder::class)
        ->and($ext->rules())->toBe([]);
});

it('SettingsExtension parent fields() and rules() return defaults when not overridden', function (): void {
    $ext = new MinimalSettingsExtension;

    expect($ext->fields())->toBeInstanceOf(FieldBuilder::class)
        ->and($ext->rules())->toBe([]);
});

it('authProvider registers and retrieves provider classes', function (): void {
    $registry = new Registry;

    $result = $registry->authProvider(AuthenticationProvider::class);

    expect($result)->toBe($registry)
        ->and($registry->getAuthProviders())->toContain(AuthenticationProvider::class);
});
