<?php

declare(strict_types=1);

namespace apexl\Io\modules\tabulator\Components;

use apexl\Io\includes\HookManager;
use apexl\Io\includes\RouteManager;
use apexl\Io\modules\component\Collections\ComponentCollection;
use apexl\Io\modules\component\Collections\ComponentPropertyCollection;
use apexl\Io\modules\component\Component\Component;
use apexl\Io\modules\component\Factories\ComponentPropertyFactory;
use apexl\Io\modules\tabulator\Collections\TabulatorActionCollection;
use apexl\Io\modules\tabulator\Collections\TabulatorColumnDtoCollection;
use apexl\Io\modules\tabulator\Collections\TabulatorFilterCollection;
use apexl\Io\modules\tabulator\Collections\TabulatorSortCollection;
use apexl\Io\modules\tabulator\Dto\TabulatorColumnDto;
use apexl\Io\modules\tabulator\Dto\TabulatorOnCellClickAction;
use apexl\Io\modules\tabulator\Dto\TabulatorOnRowClickAction;
use apexl\Io\modules\tabulator\Factories\TabulatableActionFactory;
use apexl\Io\modules\tabulator\Interfaces\TabulatorAction;
use apexl\Utils\Class\ClassBasename;
use Arrayy\Type\StringCollection;

abstract readonly class Tabulator extends Component
{
    public const string NAME = 'tabulator';

    public final const string HOOK__INIT = 'tabulator.init';

    private const string PROP__URL = 'url';
    private const string PROP__AJAX_URL = 'ajaxUrl';
    private const string PROP__COLUMNS = 'columns';
    private const string PROP__ACTIONS = 'actions';
    private const string PROP__INITIAL_HEADER_FILTER = 'initialHeaderFilter';
    private const string PROP__INITIAL_SORT = 'initialSort';
    private const string PROP__ROUTE_SAVE_LAYOUT = 'saveLayoutRoute';
    private const string PROP__ACTIVE_COLUMNS = 'activeColumns';
    private const string PROP__VIEW_ID = 'viewId';
    private const string PROP__IMMUTABLE_FILTERS = 'immutableFilters';
    private const string PROP__CAN_SELECT_COLUMNS = 'canSelectColumns';
    private const string PROP__HIDDEN_COLUMNS = 'hiddenColumns';
    private const string PROP__PAGINATION = 'pagination';
    private const string PROP__PERSISTENCE = 'persistence';
    private const string PROP__ON_CELL_CLICK_ACTIONS = 'onCellClickActions';
    private const string PROP__ON_ROW_CLICK_ACTIONS = 'onRowClickActions';

    protected const bool DISABLE_HEADER_SORT = false;

    public function __construct(
        private HookManager $hookManager,
        protected RouteManager $routeManager,
        protected TabulatableActionFactory $tabulatorActionFactory,
        ComponentPropertyFactory $propertyFactory,
        ComponentPropertyCollection $props,
        ComponentCollection $components,
        StringCollection $classes,
    ) {
        parent::__construct($propertyFactory, $props, $components, $classes);
    }

    public function canSelectColumns(bool $canSelectColumns = true): Tabulator
    {
        return $this->setProperty(self::PROP__CAN_SELECT_COLUMNS, $canSelectColumns);
    }

    public function hideColumns(array $hiddenColumns): Tabulator
    {
        return $this->setProperty(self::PROP__HIDDEN_COLUMNS, $hiddenColumns);
    }

    public function setImmutableFilters(TabulatorFilterCollection|array $immutableFilters): Tabulator
    {
        if (!($immutableFilters instanceof TabulatorFilterCollection)) {
            $immutableFilters = TabulatorFilterCollection::createFromArray($immutableFilters);
        }

        return $this->setProperty(self::PROP__IMMUTABLE_FILTERS, $immutableFilters->toArray());
    }

    public function addOnRowClickAction(TabulatorOnRowClickAction $action): Tabulator
    {
        return $this->setProperty(
            self::PROP__ON_ROW_CLICK_ACTIONS,
            [
                ...$this->props->get(self::PROP__ON_ROW_CLICK_ACTIONS) ?? [],
                $action,
            ]
        );
    }

    public function addOnCellClickAction(TabulatorOnCellClickAction $action): Tabulator
    {
        return $this->setProperty(
            self::PROP__ON_CELL_CLICK_ACTIONS,
            [
                ...$this->props->get(self::PROP__ON_CELL_CLICK_ACTIONS) ?? [],
                $action,
            ]
        );
    }

    public function init(): void
    {
        $this->setViewId($this->viewId());
        $this->setActiveColumns(new StringCollection());
        $this->addClass(implode(' ', $this->classes()));
        $this->hookManager->processHook(self::HOOK__INIT, $this);
    }

    protected function setViewId(string $viewId): void
    {
        $this->setProperty(self::PROP__VIEW_ID, $viewId);
    }

    abstract public function viewId(): string;

    public function setActiveColumns(StringCollection $columns): void
    {
        $this->setProperty(self::PROP__ACTIVE_COLUMNS, $columns->toArray());
    }

    private function classes(): array
    {
        $classes = [];

        if (self::class !== static::class) {
            $classes[] = sprintf(
                "tabulator--%s",
                lcfirst(ClassBasename::classBasename(static::class)),
            );
        }

        return $classes;
    }

    public function setSaveLayoutRoute(string $saveLayoutRoute): void
    {
        $this->setProperty(self::PROP__ROUTE_SAVE_LAYOUT, $saveLayoutRoute);
    }

    public function addAction(TabulatorAction|string $action, array $params = []): Tabulator
    {
        if (is_string($action)) {
            $action = $this->tabulatorActionFactory->make($action, $params);
        }

        return $this->setProperty(self::PROP__ACTIONS, $this->getActions()->add($action));
    }

    private function getActions(): TabulatorActionCollection
    {
        return $this->getProperty(self::PROP__ACTIONS)->value ?? new TabulatorActionCollection();
    }

    protected function setPagination(bool $pagination): void
    {
        $this->setProperty(self::PROP__PAGINATION, $pagination);
    }

    protected function setPersistence(bool $persistence): void
    {
        $this->setProperty(self::PROP__PERSISTENCE, $persistence);
    }

    protected function setUrl(string $url): void
    {
        $this->setProperty(self::PROP__URL, $url);
    }

    protected function setAjaxUrl(string $ajaxUrl): void
    {
        $this->setProperty(self::PROP__AJAX_URL, $ajaxUrl);
    }

    protected function setColumns(TabulatorColumnDtoCollection $columns): void
    {
        if (static::DISABLE_HEADER_SORT) {
            $columns = $columns->map(fn(TabulatorColumnDto $column) => $column->notSortable());
        }

        $this->setProperty(self::PROP__COLUMNS, $columns->toArray());
    }

    protected function setInitialHeaderFilter(TabulatorFilterCollection $filter): void
    {
        $this->setProperty(self::PROP__INITIAL_HEADER_FILTER, $filter);
    }

    protected function setInitialSort(TabulatorSortCollection $sort): void
    {
        $this->setProperty(self::PROP__INITIAL_SORT, $sort->forTabulator());
    }
}
