<?php

namespace apexl\Io\modules\display\includes;

use apexl\ClassEngine\EngineSingleton;
use apexl\Config\Singleton as Config;
use apexl\Io\includes\System;
use apexl\Io\modules\component\entities\componentEntity;
use apexl\Io\modules\component\services\componentBucket;
use apexl\Io\modules\display\components\TitleTile;
use apexl\Io\modules\display\regions\ContentRegion;
use apexl\Io\modules\display\regions\PageContentRegion;
use apexl\Io\modules\menu\services\Menu;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\services\currentUser;
use apexl\Io\services\pathUtility;
use Exception;
use Stringable;

abstract class Page extends EngineSingleton implements Stringable
{
    public string $pageTitle = '';
    protected array $data = [];
    protected object $metadata;
    protected Config $config;
    protected componentBucket $components;
    protected Menu $menu;
    protected ?userEntity $currentUser;
    protected pathUtility $path;
    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
     */
    protected function __construct()
    {
        parent::__construct();
        $this->metadata = (object) [];
        $this->config = Config::getInstance();
        $this->currentUser = currentUser::getCurrentUser();
        $this->menu = Menu::getInstance();
        $this->path = pathUtility::getInstance();
        $this->components = System::makeRegisteredService(componentBucket::class);

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

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

    public function __get($name)
    {
        return $this->data[$name] ?? null;
    }

    public function __set($name, $value)
    {
        $this->data[$name] = $value;
    }

    public function __isset($name)
    {
        return isset($this->data[$name]);
    }

    public function __unset($name)
    {
        unset($this->data[$name]);
    }

    public function setMetaDescription($desc): static
    {
        $this->metadata->description = $desc;

        return $this;
    }

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

        return $this;
    }

    public function registerTitleComponent($component)
    {
        $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;
    }

    /**
     * Get the top level component by name.
     * @TODO change how components are registered maybe?
     */
    public function getComponent(string $name): componentEntity|false
    {
        return $this->components->get($name);
    }

    /**
     * Allow top level components to be replaced
     */
    public function replaceComponent(string $name, componentEntity $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(componentEntity $component): static
    {
        $name = $component->getName();

        $this->components->set($name, $component);

        return $this;
    }

    public function __toString(): string
    {
        return json_encode($this->getPage());
    }

    /**
     * Get the page as an MD array
     * @return array
     */
    public function getPage(): array
    {
        $this->addComponent($this->globalRegion->buildRegions());
        $this->setMetaTitle($this->pageTitle.' | '.($this->config->app->defaultMetaTitle ?? "Io Dashboard"));
        $data = [];
        $data['metadata'] = $this->metadata;
        foreach ($this->components as $component) {
            //we need to decode the cast components as they will be json encoded.
            //We do this to ensure we have all nested data.
            $data['components'][] = json_decode((string) $component);
        }

        return $data;
    }

    public function setMetaTitle($title): static
    {
        $this->metadata->title = $title;

        return $this;
    }
}
