<?php

declare(strict_types=1);

namespace apexl\Io\modules\tabulator;

use apexl\Config\ConfigurationInterface;
use apexl\Io\enums\HttpMethod;
use apexl\Io\exceptions\RouteNotFoundException;
use apexl\Io\includes\HookManager;
use apexl\Io\includes\Module;
use apexl\Io\includes\RouteManager;
use apexl\Io\modules\menu\interfaces\HasMenuEntryInterface;
use apexl\Io\modules\system\interfaces\ProvidesRoutesInterface;
use apexl\Io\modules\tabulator\Collections\HasTabulatorViewInterfaceCollection;
use apexl\Io\modules\tabulator\Factories\TabulatorAjaxControllerFactory;
use apexl\Io\modules\tabulator\Factories\TabulatorControllerFactory;
use apexl\Io\modules\tabulator\Factories\TabulatorDisplayControllerFactory;
use apexl\Io\modules\tabulator\Interfaces\HasTabulatorViewInterface;
use apexl\Io\services\InstallChecker;
use app\vendor\apexl\utils\src\Utils\Strings\DashCase;
use DI\DependencyException;
use DI\NotFoundException;

final class tabulatorModule extends Module implements ProvidesRoutesInterface
{
    public const string HOOK__COLLECT_ENTITIES = 'hook.collect_entities';

    public function __construct(
        InstallChecker $installChecker,
        ConfigurationInterface $config,
        readonly private HookManager $hookManager,
        readonly private TabulatorControllerFactory $tabulatorControllerFactory,
        readonly private TabulatorAjaxControllerFactory $tabulatorAjaxControllerFactory,
        readonly private TabulatorDisplayControllerFactory $tabulatorDisplayControllerFactory,
    ) {
        parent::__construct($installChecker, $config);
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException|RouteNotFoundException
     */
    public function routes(RouteManager $routeManager): void
    {
        $this->tabulatableRoutes($routeManager);
        $this->accountRoutes($routeManager);
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException
     */
    private function tabulatableRoutes(RouteManager $routeManager): void
    {


        $this->entities()->each(fn(HasTabulatorViewInterface $hasTabulatorView) => $this->tabulatableRoute(
            $routeManager,
            $hasTabulatorView
        ));
    }

    private function entities(): HasTabulatorViewInterfaceCollection
    {
        return $this->hookManager->processHook(
            self::HOOK__COLLECT_ENTITIES,
            new HasTabulatorViewInterfaceCollection()
        );
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException
     */
    private function tabulatableRoute(RouteManager $routeManager, HasTabulatorViewInterface $entity): void
    {
        $name = DashCase::dashCase($entity::shortName());

        $this->addRoute(
            $routeManager,
            HttpMethod::GET,
            $entity::tabulatorRouteName(),
            sprintf('data/tabulator/%s', $name),
            $this->tabulatorControllerFactory->make($entity),
        );

        $this->addRoute(
            $routeManager,
            HttpMethod::GET,
            $entity::tabulatorAjaxRouteName(),
            sprintf('data/tabulator/ajax/%s', $name),
            $this->tabulatorAjaxControllerFactory->make($entity),
        );
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException
     * @throws RouteNotFoundException
     */
    private function accountRoutes(RouteManager $routeManager): void
    {
        foreach ($this->entities() as $entity) {
            $this->accountRoute($routeManager, $entity);
        }
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException
     * @throws RouteNotFoundException
     */
    private function accountRoute(RouteManager $routeManager, HasMenuEntryInterface $entity): void
    {
        $name = $entity->menuRoute();

        $this->rootRoute(
            $routeManager,
            HttpMethod::GET,
            $name,
            $entity->menuPattern(),
            $this->tabulatorDisplayControllerFactory->make($entity::class),
        );

        $routeManager->addRouteArg($name, 'permission', serialize($entity->menuPermission()));
    }

}
