<?php

namespace apexl\Io\modules\display\includes;

use apexl\Io\includes\HookManager;
use apexl\Io\modules\component\Collections\ComponentCollection;
use apexl\Io\modules\component\Component\Component;
use apexl\Io\modules\component\Factories\GenericComponentFactory;
use apexl\Io\modules\component\Interfaces\ComponentInterface;
use apexl\Io\modules\display\components\TitleTile;
use apexl\Io\modules\display\Dtos\PageDataDto;
use apexl\Io\modules\display\regions\ContentRegion;
use apexl\Io\modules\display\regions\PageContentRegion;
use apexl\Io\modules\menu\services\MenuManager;
use apexl\Io\services\HttpPaths;
use Exception;
use JsonSerializable;
use function apexl\Io\config;

;

abstract class Page implements JsonSerializable
{
    public const string HOOK__GLOBAL_DATA = 'page.global_data';
    public const string HOOK__METADATA = 'page.metadata';
    public const string HOOK__COMPONENTS = 'page.components';

    public string $pageTitle = '';
    protected array $metadata = [];
    protected ComponentCollection $components;
    protected TitleTile $titleTile;

    /** While the page has its own "components" array, we're working in regions so need a holder. */
    protected region $globalRegion;

    /**
     * @throws Exception
     */
    public function __construct(
        protected MenuManager $menuManager,
        protected HttpPaths $path,
        protected GenericComponentFactory $componentFactory,
        protected HookManager $hookManager,
    ) {
        $this->components = new ComponentCollection();

        //do this in a method so extending classes can override.
        $route = $this->menuManager->getMenuItem($this->path->getCurrentRouteName());
        if ($title = $route->title ?? $this->path->getCurrentRouteName()) {
            $this->setPageTitle($title);
        }
        $this->buildRegions();
    }

    /**
     * Default must have content!
     */
    protected function buildRegions(): void
    {
        $this->globalRegion = new PageContentRegion();
        $this->globalRegion->addRegions([new ContentRegion()]);
    }

    public function setMetaDescription(string $description): static
    {
        return $this->setMetadata('description', $description);
    }

    public function setMetadata($metaKey, $metaValue): static
    {
        $this->metadata[$metaKey] = $metaValue;

        return $this;
    }

    public function registerTitleComponent(TitleTile $component): void
    {
        $this->titleTile = $component;
    }

    public function getPageTitle()
    {
        return $this->getPageTitle();
    }

    public function setPageTitle(string $title): static
    {
        $this->pageTitle = $title;
        if (isset($this->titleTile) && method_exists($this->titleTile, 'text')) {
            $this->titleTile->text($this->pageTitle);
        }

        return $this;
    }

    public function getComponent(string $name): Component|false
    {
        return $this->components->get($name);
    }

    public function replaceComponent(string $name, Component $component): bool
    {
        if ($replace = $this->components->has($name)) {
            $this->components->set($name, $component);
        }

        return $replace;
    }

    public function addToContent(array $components, ?int $weight = null): static
    {
        $this->addToRegion($components, ContentRegion::class, $weight);

        return $this;
    }

    protected function addToRegion(array $components, string $regionName, ?int $weight): static
    {
        foreach ($components as $component) {
            $this->globalRegion->addComponent($component, $weight, $regionName);
        }

        return $this;
    }

    public function addComponent(ComponentInterface $component): static
    {
        $this->components->set($component->id, $component);

        return $this;
    }

    public function jsonSerialize(): PageDataDto
    {
        return $this->getPage();
    }

    public function getPage(): PageDataDto
    {
        $this->addComponent($this->globalRegion->buildRegions());
        $this->setMetaTitle(
            sprintf("%s %s %s", $this->pageTitle, config('app.meta.default_divider'), config('app.meta.default_title'))
        );
        

        return new PageDataDto(
            $this->hookManager->processHook(self::HOOK__METADATA, $this->metadata),
            $this->hookManager->processHook(self::HOOK__GLOBAL_DATA, []),
            $this->hookManager->processHook(self::HOOK__COMPONENTS, $this->components)->values()->toArray(),
        );
    }

    public function setMetaTitle(string $title): static
    {
        return $this->setMetadata('title', $title);
    }
}
