<?php

declare(strict_types=1);

namespace CliqThemes\UI\Admin;

use CliqThemes\UI\WordPress\Integration;

/**
 * Collects admin page definitions and wires them into WordPress.
 */
final class AdminPageRegistry
{
    private Integration $integration;

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

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

    private bool $booted = false;

    /**
     * @param Integration $integration WordPress integration responsible for enqueuing assets.
     */
    public function __construct(Integration $integration)
    {
        $this->integration = $integration;
    }

    /**
     * Adds a page to the registry and ensures WordPress hooks are initialised.
     */
    public function register(AdminPage $page): void
    {
        $page = $this->normaliseScriptHandle($page);

        $this->pages[$page->slug()] = $page;

        $this->ensureBootstrapped();
    }

    /**
     * Registers multiple pages in a single call.
     *
     * @param iterable<int,AdminPage> $pages
     */
    public function registerMany(array $pages): void
    {
        foreach ($pages as $page) {
            if (! $page instanceof AdminPage) {
                continue;
            }

            $this->register($page);
        }
    }

    /**
     * Called by WordPress to register menu entries for every stored page definition.
     */
    public function registerMenuItems(): void
    {
        if (! function_exists('add_menu_page')) {
            return;
        }

        foreach ($this->pages as $page) {
            $hook = $this->registerPageWithWordPress($page);
            if ($hook === null) {
                continue;
            }

            $this->hookedPages[$hook] = $page;
            $this->integration->allowPageHook($hook);
        }
    }

    /**
     * Enqueues the configured assets when a registered page is rendered.
     */
    public function handleEnqueue(string $hookSuffix): void
    {
        if (! isset($this->hookedPages[$hookSuffix])) {
            return;
        }

        $page = $this->hookedPages[$hookSuffix];

        if (function_exists('wp_enqueue_script')) {
            wp_enqueue_script($page->scriptHandle());
        }

        $styleHandle = $this->integration->styleHandle();
        if (
            $styleHandle !== ''
            && function_exists('wp_style_is')
            && wp_style_is($styleHandle, 'registered')
            && function_exists('wp_enqueue_style')
        ) {
            wp_enqueue_style($styleHandle);
        }
    }

    /**
     * Lazily hooks WordPress actions the first time a page is registered.
     */
    private function ensureBootstrapped(): void
    {
        if ($this->booted) {
            return;
        }

        $this->booted = true;

        add_action('admin_menu', [$this, 'registerMenuItems']);
        add_action('admin_enqueue_scripts', [$this, 'handleEnqueue'], 20, 1);
    }

    /**
     * Registers the page with WordPress and returns the resulting hook suffix.
     */
    private function registerPageWithWordPress(AdminPage $page): ?string
    {
        $callback = function () use ($page): void {
            $this->renderPage($page);
        };

        if ($page->parentSlug()) {
            return $this->registerSubmenuPage($page, $callback);
        }

        return $this->registerTopLevelPage($page, $callback);
    }

    /**
     * Registers a top-level menu page.
     */
    private function registerTopLevelPage(AdminPage $page, callable $callback): ?string
    {
        $icon = $page->iconUrl() ?? 'dashicons-admin-generic';

        $hook = add_menu_page(
            $page->pageTitle(),
            $page->menuTitle(),
            $page->capability(),
            $page->slug(),
            $callback,
            $icon,
            $page->position()
        );

        return is_string($hook) ? $hook : null;
    }

    /**
     * Registers a submenu page under the configured parent slug.
     */
    private function registerSubmenuPage(AdminPage $page, callable $callback): ?string
    {
        $hook = add_submenu_page(
            $page->parentSlug(),
            $page->pageTitle(),
            $page->menuTitle(),
            $page->capability(),
            $page->slug(),
            $callback,
            $page->position()
        );

        return is_string($hook) ? $hook : null;
    }

    /**
     * Renders the minimal HTML wrapper that the React bundle will hydrate.
     */
    private function renderPage(AdminPage $page): void
    {
        echo '<div class="wrap cliqthemes-ui-admin-page">';
        echo '<h1>' . esc_html($page->pageTitle()) . '</h1>';

        $description = $page->description();
        if ($description !== null && $description !== '') {
            echo '<p class="description">' . esc_html($description) . '</p>';
        }

        $this->integration->renderMountPoint();
        echo '</div>';
    }

    /**
     * Ensures the script handle aligns with the integration when consumers omit a custom handle.
     */
    private function normaliseScriptHandle(AdminPage $page): AdminPage
    {
        $desiredHandle = $this->integration->scriptHandle();
        if ($page->scriptHandle() === AdminPage::DEFAULT_SCRIPT_HANDLE && $page->scriptHandle() !== $desiredHandle) {
            return $page->withScriptHandle($desiredHandle);
        }

        return $page;
    }
}
