<?php

declare(strict_types=1);

namespace apexl\Io\modules\tabulator\Components;

use apexl\Io\includes\HookManager;
use apexl\Io\includes\RouteManager;
use apexl\Io\interfaces\HasEditRouteInterface;
use apexl\Io\interfaces\HasGeneralSearchInterface;
use apexl\Io\interfaces\HasViewRouteInterface;
use apexl\Io\modules\component\Collections\ComponentCollection;
use apexl\Io\modules\component\Collections\ComponentPropertyCollection;
use apexl\Io\modules\component\Factories\ComponentPropertyFactory;
use apexl\Io\modules\tabulator\Factories\TabulatableActionFactory;
use apexl\Io\modules\tabulator\Factories\TabulatableFactory;
use apexl\Io\modules\tabulator\Interfaces\HasSubEntitiesInterface;
use apexl\Io\modules\tabulator\Interfaces\HasTabulatorViewInterface;
use apexl\Io\modules\tabulator\Interfaces\HasTreeStructureInterface;
use apexl\Io\modules\tabulator\TabulatorActions\Link;
use apexl\Io\services\HttpPaths;
use Arrayy\Type\StringCollection;
use DI\DependencyException;
use DI\NotFoundException;
use function apexl\Io\assertInstanceOf;

readonly class EntityTabulator extends Tabulator
{
    private const string PROP__HAS_GENERAL_SEARCH = 'hasGeneralSearch';
    private const string PROP__HAS_TREE_LAYOUT = 'hasTreeLayout';
    private const string PROP__SUBENTITY = 'subEntity';
    private const string PROP__SHOULD_POLL = 'shouldPoll';

    public const string NAME = 'EntityTabulator';

    /**
     * @param class-string<HasTabulatorViewInterface> $entityClass
     */
    public function __construct(
        HookManager $hookManager,
        RouteManager $routeManager,
        ComponentPropertyFactory $propertyFactory,
        ComponentPropertyCollection $props,
        ComponentCollection $components,
        StringCollection $classes,
        TabulatableActionFactory $tabulatorActionFactory,
        private HttpPaths $httpPaths,
        private TabulatableFactory $tabulatableFactory,
        private string $entityClass,
        private ?string $subEntityKey,
    ) {
        assertInstanceOf($entityClass, HasTabulatorViewInterface::class);

        parent::__construct(
            $hookManager,
            $routeManager,
            $tabulatorActionFactory,
            $propertyFactory,
            $props,
            $components,
            $classes,
        );
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException
     */
    public function init(): void
    {
        parent::init();
        $entity = $this->instantiateEntity();

        $tabulatable = $this->tabulatableFactory->make($entity);

        $this->setUrl(
            $this->httpPaths->backendUrl($entity->tabulatorRoute($this->routeManager))
        );

        $this->setAjaxUrl(
            $this->httpPaths->backendUrl($entity->tabulatorAjaxRoute($this->routeManager))
        );

        $this->setColumns($tabulatable->displayColumns());
        if ($this->subEntityKey && $entity instanceof HasSubEntitiesInterface) {
            $this->setSubEntityKey($this->subEntityKey);
            $this->setColumns(
                $tabulatable->filteredColumns($entity->getSubEntityFieldsByKey($this->subEntityKey))
            );
        }

        $this->setInitialHeaderFilter($tabulatable->defaultFilterCollection());
        $this->setInitialSort($tabulatable->sort());

        foreach (
            [
                HasGeneralSearchInterface::class => $this->setHasGeneralSearch(...),
                HasTreeStructureInterface::class => $this->hasTreeLayout(...),
            ] as $interface => $action
        ) {
            if (is_subclass_of($this->entityClass, $interface)) {
                $action();
            }
        }

        if ($entity instanceof HasViewRouteInterface) {
            $this->addAction($this->tabulatorActionFactory->make(
                Link::class,
                [
                    'link' => $entity->viewRouteTemplate(),
                    'icon' => 'eye'
                ]
            ));
        }

        if ($entity instanceof HasEditRouteInterface) {
            $this->addAction($this->tabulatorActionFactory->make(
                Link::class,
                [
                    'link' => $entity->editRouteTemplate(),
                    'icon' => 'pencil'
                ]
            ));
        }
    }

    private function instantiateEntity(): HasTabulatorViewInterface
    {
        return new ($this->entityClass)();
    }

    public function setSubEntityKey(string $subEntityKey): static
    {
        return $this->setProperty(self::PROP__SUBENTITY, $subEntityKey);
    }

    public function setHasGeneralSearch(bool $hasGeneralSearch = true): static
    {
        return $this->setProperty(self::PROP__HAS_GENERAL_SEARCH, $hasGeneralSearch);
    }

    public function hasTreeLayout(bool $hasTreeLayout = true): EntityTabulator
    {
        if ($hasTreeLayout && is_subclass_of($this->entityClass, HasTreeStructureInterface::class)) {
            $this->setProperty(self::PROP__HAS_TREE_LAYOUT, true);

            return $this;
        }

        return $this->removeProperty(self::PROP__HAS_TREE_LAYOUT);
    }

    public function viewId(): string
    {
        return $this->entityClass;
    }

    public function setShouldPollData(bool $shouldPoll = true): static
    {
        return $this->setProperty(self::PROP__SHOULD_POLL, $shouldPoll);
    }
}
