<?php

declare(strict_types=1);

namespace Builder\Core\Foundation\Resources;

use Builder\Core\Exceptions\DeletionDeniedException;
use Builder\Core\Foundation\Contracts\HasPublishableRecords;
use Builder\Core\Foundation\Dashboard\Widgets\Widget;
use Builder\Core\Foundation\Definition\FieldBuilder;
use Builder\Core\Foundation\Definition\Hook;
use Builder\Core\Foundation\Enums\Icon;
use Builder\Core\Foundation\Tables\BulkAction;
use Builder\Core\Foundation\Tables\Column;
use Builder\Core\Foundation\Tables\Filter;
use Builder\Core\Foundation\Tables\RowAction;
use Illuminate\Database\Eloquent\Model;

abstract class Resource
{
    abstract public static function getSlug(): string;

    abstract public static function getLabel(): string;

    /**
     * Singular form of the label, used in e.g. "Add new post".
     * Defaults to getLabel(). Override to provide a proper singular form.
     */
    public static function getSingularLabel(): string
    {
        return static::getLabel();
    }

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

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

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

    /** @return array<string, mixed> */
    public function rules(string $action): array
    {
        return [];
    }

    /** @return list<Hook> */
    public function hooks(): array
    {
        return [];
    }

    /** @return list<Column> */
    public function columns(): array
    {
        return [];
    }

    /** @return list<Filter> */
    public function filters(): array
    {
        return [];
    }

    /** @return list<BulkAction> */
    public function bulkActions(): array
    {
        return [];
    }

    /**
     * Assert that the given record may be deleted.
     * Throw DeletionDeniedException with a human-readable reason to prevent deletion.
     */
    public function canDelete(mixed $record): void
    {
        //
    }

    /** @return list<RowAction> */
    public function rowActions(): array
    {
        return [];
    }

    /** @return list<RecordAction> */
    public function recordActions(): array
    {
        return [];
    }

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

    /** @return list<Widget> */
    public function widgets(): array
    {
        return [];
    }

    /**
     * The permission key required to access this resource.
     * Return null to allow any authenticated builder user.
     */
    public function getPermission(): ?string
    {
        return null;
    }

    /**
     * Per-action permission map, e.g. ['index' => 'read:users', 'store' => 'create:users'].
     * When provided, overrides getPermission() for the listed actions.
     * Actions not listed fall back to getPermission().
     *
     * @return array<string, string>
     */
    public function permissions(): array
    {
        return [];
    }

    /**
     * All permissions this resource requires. Used by InstallCommand to seed permissions.
     *
     * @return list<string>
     */
    public function getPermissionSet(): array
    {
        return [];
    }

    /**
     * Whether to show created_at/updated_at timestamps in the form header.
     */
    public function showTimestamps(): bool
    {
        return true;
    }

    /**
     * Sidebar menu priority. Return an integer to auto-register a menu item.
     * Return null (default) to skip auto-registration — register manually instead.
     */
    public function getMenuPriority(): ?int
    {
        return null;
    }

    /**
     * Icon shown in the sidebar menu item for this resource.
     * Only used when getMenuPriority() is non-null.
     */
    public function getMenuIcon(): Icon
    {
        return Icon::FileText;
    }

    /**
     * Whether this resource has a parent-child hierarchy (parent_id).
     * When true, slug generation automatically prepends the parent's slug,
     * and depth indentation is shown in the index table.
     */
    public function isHierarchical(): bool
    {
        return false;
    }

    /**
     * The column name that stores the parent FK.
     * Defaults to 'parent_id' — override when your column name differs.
     */
    public function getParentField(): string
    {
        return 'parent_id';
    }

    /**
     * Whether this resource has public-facing URLs (e.g. pages, blog posts).
     * When true, a "View" link appears in the index table next to each record.
     */
    public function isPublic(): bool
    {
        return false;
    }

    /**
     * URL prefix for the public-facing view link shown in the index table.
     * For root-level pages use '' (empty string). For blog posts use '/blog'.
     * The full URL is: {publicUrlBase}/{row.slug}
     */
    public function getPublicUrlBase(): string
    {
        return '';
    }

    /**
     * Override to inject extra data into the record before the edit form is rendered.
     * Use this to load nested resources, computed data, or any extra fields.
     *
     * @return array<string, mixed>
     */
    public function getRecordForEdit(Model $record): array
    {
        return $record->toArray();
    }

    /**
     * Whether this resource supports one-click duplication from the index table.
     * When true, a "Duplicate" row action is automatically injected into the ⋯ menu.
     */
    protected bool $duplicatable = false;

    public function isDuplicatable(): bool
    {
        return $this->duplicatable;
    }

    /**
     * Create a duplicate of the given record.
     * Override to apply custom logic (prefix title, clear status, skip fields, etc.).
     * The returned model will be saved by the controller — do not save it here.
     */
    public function duplicate(mixed $record): mixed
    {
        return $record->replicate();
    }

    /**
     * Called after the clone has been saved.
     * Override to copy relationships, translations, or other post-save data.
     */
    public function postDuplicate(mixed $original, mixed $clone): void
    {
        // Default: no-op
    }

    public function isPublishable(): bool
    {
        return $this instanceof HasPublishableRecords;
    }

    /**
     * Whether changes to this resource's records should be tracked as revisions.
     * Override to return true in resources that need revision history.
     */
    public function isRevisable(): bool
    {
        return false;
    }

    /**
     * Maximum number of revisions to retain per record.
     * Older revisions are pruned automatically after each save.
     * Defaults to the global config value (builder.revisions.keep).
     */
    public function getMaxRevisions(): int
    {
        return (int) config('builder.revisions.keep', 10);
    }
}
