<?php

namespace WordpressModels\Page;

use Timber\Timber;
use WordpressModels\Assets;

/**
 *
 */
abstract class AbstractPage
{
    protected string|false $pageLoadHook;
    protected string|array|\Closure $renderCallback;

    /**
     * @param string[] $additionalScripts
     */
    public function __construct(
        protected string              $pageId,
        protected string              $menuSlug,
        protected string              $title,
        protected string|AbstractPage $parent = 'toplevel',
        protected string              $capability = 'administrator',
        protected int|string|null     $position = null,
        protected string              $icon = '',
        protected array               $additionalScripts = [],
        protected readonly ?Assets    $assets = null
    )
    {
        $this->renderCallback = [$this, 'renderPage'];
        add_action('admin_menu', $this->addAdminMenuItem(...));
    }

    /**
     * Initializes the page.
     * @return void
     * @hooked load-{parent}_page_{slug}
     */
    public function init(): void
    {
        add_action('admin_enqueue_scripts', $this->enqueueScripts(...));
    }

    /**
     * Renders the page.
     *
     * @return void
     */
    public function renderPage(): void
    {
        $context = $this->buildContext(
            array_merge(Timber::context(), [
                'pageId' => $this->pageId,
                'title' => $this->title
            ])
        );
        Timber::render("page-$this->pageId.twig", $context);
    }

    /**
     * Build the context for the Twig template.
     *
     * @param array $context Base context provided with Timber::context(), the page ID and title.
     * @return mixed
     */
    abstract public function buildContext(array $context = []): mixed;

    public function enqueueScripts(): void
    {
        if ($this->assets) {
            $this->assets->enqueueCompiledScript($this->pageId);

            wp_localize_script($this->pageId, 'page', [
                'id' => $this->pageId,
                'title' => $this->title,
                'baseUrl' => $_SERVER['REQUEST_URI'],
            ]);

            foreach ($this->additionalScripts as $script) {
                $this->assets->enqueueCompiledScript($script);
            }
        }

        $this->doEnqueue();
    }


    abstract public function doEnqueue();

    public function addAdminMenuItem(): void
    {
        if ($this->parent !== 'toplevel') {
            $this->pageLoadHook = add_submenu_page(
                $this->parent instanceof AbstractPage ? $this->parent->getMenuSlug() : $this->parent,
                $this->title,
                $this->title,
                $this->capability,
                $this->menuSlug,
                $this->renderCallback,
                $this->position ?: '0'
            );
        } else {
            $this->pageLoadHook = add_menu_page(
                $this->title,
                $this->title,
                $this->capability,
                $this->menuSlug,
                $this->renderCallback,
                $this->icon,
                $this->position ?: '0'
            );
        }

        if ($this->pageLoadHook) {
            add_action('load-' . $this->pageLoadHook, $this->init(...));
        }
    }

    /**
     * @return string
     */
    public function getPageId(): string
    {
        return $this->pageId;
    }

    /**
     * @return string
     */
    public function getParent(): string
    {
        return $this->parent instanceof AbstractPage ? $this->parent->menuSlug : $this->parent;
    }

    /**
     * @return string
     */
    public function getMenuSlug(): string
    {
        return $this->menuSlug;
    }

    /**
     * @return string
     */
    public function getCapability(): string
    {
        return $this->capability;
    }

    /**
     * @return string
     */
    public function getTitle(): string
    {
        return $this->title;
    }

    /**
     * @return float|int|null
     */
    public function getPosition(): float|int|null
    {
        return $this->position;
    }

    /**
     * @return string
     */
    public function getIcon(): string
    {
        return $this->icon;
    }

}
