<?php

declare(strict_types=1);

namespace apexl\Io\modules\tabulator\Controllers;

use apexl\Io\modules\component\Factories\GenericComponentFactory;
use apexl\Io\modules\display\components\defaultWrapper;
use apexl\Io\modules\display\Controllers\DisplayController;
use apexl\Io\modules\display\services\Render;
use apexl\Io\modules\tabulator\ComponentFactory\EntityTabulatorFactory;
use apexl\Io\modules\tabulator\Components\EntityTabulator;
use apexl\Io\modules\tabulator\Interfaces\HasSubEntitiesInterface;
use apexl\Io\modules\tabulator\Interfaces\HasTabulatorViewInterface;
use apexl\Io\interfaces\OutputManagerInterface;
use apexl\Utils\Class\ClassBasename;
use app\module\newtonsTrailers\Interface\HasTabulatorPageFiltersInterface;
use app\module\newtonsTrailersDisplay\Component\TabulatorPageFilter;
use DI\DependencyException;
use DI\NotFoundException;
use JsonException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use function apexl\Io\assertInstanceOf;

readonly class TabulatorDisplayController extends DisplayController
{
    /**
     * @param class-string<HasTabulatorViewInterface> $entity
     */
    public function __construct(
        private string $entity,
        protected EntityTabulatorFactory $entityTabulatorFactory,
        Render $render,
        OutputManagerInterface $output,
        GenericComponentFactory $componentFactory
    ) {
        assertInstanceOf($entity, HasTabulatorViewInterface::class);

        parent::__construct($componentFactory, $render, $output);
    }

    /**
     * @throws DependencyException
     * @throws JsonException
     * @throws NotFoundException
     */
    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
    ): ResponseInterface {
        $this->render->setMetaAndPageTitle(($this->entity)::tabulatorListTitle());

        $this->output->addResponse(
            $request,
            $this->render->build(
                [
                    $this->component(defaultWrapper::class)
                        ->addClass(implode(' ', $this->wrapperClasses()))
                        ->addComponents($this->components()),
                ]
            )
        );

        return $this->json($response);
    }

    protected function wrapperClasses(): array
    {
        $classes = [
            'tabulatorDisplayWrapper',
        ];

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

        return $classes;
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException
     */
    protected function components(): array
    {
        $components = [];

        if (is_subclass_of($this->entity, HasTabulatorPageFiltersInterface::class)) {
            $components[] = $this->component(
                TabulatorPageFilter::class,
                [(new $this->entity())->getTabulatorPageFilters()]
            );
        }

        return array_merge(
            $components,
            [
                $this->tabulator(),
            ]
        );
    }

    /**
     * @throws DependencyException
     * @throws NotFoundException
     */
    protected function tabulator($subEntityKey = null): EntityTabulator
    {
        return $this->entityTabulatorFactory->make($this->entity, $subEntityKey);
    }

    protected function subEntityComponents(): array
    {
        if (is_subclass_of($this->entity, HasSubEntitiesInterface::class)) {

            foreach (array_keys((new $this->entity())->subEntityFields()) as $subEntityKey) {
                $components[] = $this->tabulator($subEntityKey);
            }
        }

        return $components ?? [];
    }
}
