<?php

namespace WordpressModels\DependencyInjection\Pages;

use WordpressModels\DependencyInjection\HookAttributes\Attributes\Action;
use WordpressModels\Page\AbstractPage;
use WordpressModels\Page\AbstractPageStack;

/**
 * Registry for all pages.
 *
 * Pages are registered as services with the tag 'wpm_page' and must implement PageInterface.
 */
class PageRegistry
{

    /**
     * @var array<string, AbstractPage>
     */
    private array $pages = [];

    /**
     * @var array<string, string>
     */
    private array $loadHooks = [];

    public function __construct()
    {
    }

    /**
     * @return array<string, AbstractPage>
     */
    public function getPages(): array
    {
        return $this->pages;
    }

    public function getPage(string $pageId): ?AbstractPage
    {
        return $this->pages[$pageId] ?? null;
    }

    public function addPage(AbstractPage $page)
    {
        $this->pages[$page->getPageId()] = $page;
    }

    /**
     * @return array
     */
    public function getLoadHooks(): array
    {
        return $this->loadHooks;
    }

    #[Action('admin_menu')]
    public function registerPages()
    {
        // All pages that are routes of a PageStack
        /** @var AbstractPage[] $routePages */
        $routePages = [];
        $sortedPages = [];
        foreach ($this->pages as $page) {
            if (!$page instanceof AbstractPageStack) {
                $sortedPages[] = $page;
                continue;
            }
            // prepend
            array_unshift($sortedPages, $page);
            $routePages = array_merge($routePages, $page->getPages());
        }
        $routePageIds = array_map(fn($page) => $page->getPageId(), $routePages);


        foreach ($sortedPages as $page) {
            $parent = $page->getParent();
            if (!$parent) {
                continue;
            }
            // the render callback is an empty string if the page is a route of a PageStack
            $renderCallback = in_array($page->getPageId(), $routePageIds) ? '' : [$page, 'renderPage'];

            if ($parent !== 'toplevel') {
                $pageLoadHook = add_submenu_page(
                    $parent instanceof AbstractPage ? $parent->getMenuSlug() : $parent,
                    $page->getTitle(),
                    $page->getTitle(),
                    $page->getCapability(),
                    $page->getMenuSlug(),
                    $renderCallback,
                    $page->getPosition() ?: '0'
                );
            } else {
                $pageLoadHook = add_menu_page(
                    $page->getTitle(),
                    $page->getTitle(),
                    $page->getCapability(),
                    $page->getMenuSlug(),
                    $renderCallback,
                    $page->getIcon(),
                    $page->getPosition() ?: '0'
                );
            }

            if ($pageLoadHook) {
                $this->loadHooks[$page->getPageId()] = $pageLoadHook;
                add_action('load-' . $pageLoadHook, $page->init(...));
            }
        }
    }

    public function getParentPage(AbstractPage $page): ?AbstractPageStack
    {
        foreach ($this->pages as $parentPage) {
            if (!$parentPage instanceof AbstractPageStack) {
                continue;
            }

            if (in_array($page->getPageId(), array_map(fn($page) => $page->getPageId(), $parentPage->getPages()))) {
                return $parentPage;
            }
        }

        return null;
    }

}
