<?php
declare(strict_types=1);

namespace Ekesto\CacheWarmup;

use Ekesto\CacheWarmup\ImmediateKick;
use Ekesto\CacheWarmup\Settings\Actions\AdminBarAction;
use Ekesto\CacheWarmup\Settings\Actions\RunNowAction;
use Ekesto\CacheWarmup\Settings\Actions\SitemapRefreshAction;
use Ekesto\CacheWarmup\Settings\AdminBarMenu;
use Ekesto\CacheWarmup\Settings\AdminPage;
use Ekesto\CacheWarmup\Settings\PriorityPagesCache;
use Ekesto\CacheWarmup\Settings\Repository;
use Ekesto\CacheWarmup\Settings\RestController;
use Ekesto\CacheWarmup\Support\Access;

/**
 * Warmup runtime bootstrap.
 * Stores base paths and wires module registration.
 */
final class WarmupRuntime
{
    /** @var string */
    private static string $baseUrl;

    /** @var string */
    private static string $basePath;

    /** @var bool */
    private static bool $runtimeHooksRegistered = false;

    /** @var bool */
    private static bool $adminHooksRegistered = false;

    /** @var bool */
    private static bool $restHooksRegistered = false;

    /** @var bool */
    private static bool $adminBarHooksRegistered = false;

    /**
     * Return the minimum class set required to boot safely.
     *
     * @return array<int,string>
     */
    public static function bootRequiredClasses(): array
    {
        return [
            self::class,
            Lifecycle::class,
            Logger::class,
            Debug::class,
        ];
    }

    /**
     * Initialize the plugin and register all modules.
     *
     * @param string $baseUrl  Absolute plugin URL.
     * @param string $basePath Absolute plugin path.
     * @return void
     */
    public static function boot(string $baseUrl, string $basePath): void
    {
        self::$baseUrl = rtrim($baseUrl, '/') . '/';
        self::$basePath = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;

        add_action('plugins_loaded', [__CLASS__, 'registerRuntimeHooks'], 20);
        add_action('plugins_loaded', [__CLASS__, 'registerAdminHooks'], 15);
        add_action('plugins_loaded', [__CLASS__, 'registerAdminBarHooks'], 15);
        add_action('rest_api_init', [__CLASS__, 'registerRestHooks'], 1);
        add_action('admin_post_ekesto_cache_warmup_reset_queue', [__CLASS__, 'handleResetQueue']);
    }

    /**
     * Register runtime modules required for queueing and cron processing.
     *
     * @return void
     */
    public static function registerRuntimeHooks(): void
    {
        if (self::$runtimeHooksRegistered) {
            return;
        }

        foreach ([Triggers::class, Runner::class, PriorityPagesCache::class] as $registrarClass) {
            if (!class_exists($registrarClass, true)) {
                self::logBootstrapIssue(sprintf('runtime module skipped: missing class %s', $registrarClass));
                return;
            }

            $registrarClass::register();
        }

        self::$runtimeHooksRegistered = true;
    }

    /**
     * Register admin-only hooks lazily.
     *
     * @return void
     */
    public static function registerAdminHooks(): void
    {
        if (self::$adminHooksRegistered || !is_admin()) {
            return;
        }

        $requiredClasses = [
            AdminPage::class,
            Repository::class,
            RunNowAction::class,
            SitemapRefreshAction::class,
        ];

        foreach ($requiredClasses as $requiredClass) {
            if (!class_exists($requiredClass, true)) {
                self::logBootstrapIssue(sprintf('admin module skipped: missing class %s', $requiredClass));
                return;
            }
        }

        add_action('admin_menu', [AdminPage::class, 'addMenuPage'], 20);
        add_filter('custom_menu_order', [AdminPage::class, 'enableCustomMenuOrder'], PHP_INT_MAX);
        add_filter('menu_order', [AdminPage::class, 'moveTopLevelMenuToEnd'], PHP_INT_MAX);
        add_action('admin_enqueue_scripts', [AdminPage::class, 'enqueueAdminAssets']);
        add_action('admin_notices', [AdminPage::class, 'renderAdminNotices']);
        add_filter('removable_query_args', [AdminPage::class, 'addRemovableNoticeQueryArgs']);
        add_action('admin_post_ekesto_cache_warmup_run_now', [RunNowAction::class, 'handleAdminPost']);
        add_action('admin_post_' . SitemapRefreshAction::ACTION, [SitemapRefreshAction::class, 'handleAdminPost']);
        add_action('admin_init', [Repository::class, 'registerOption']);

        $filter = 'plugin_action_links_' . plugin_basename(EKESTO_CA_PLUGIN_FILE);
        add_filter($filter, [AdminPage::class, 'addPluginSettingsLink']);

        self::$adminHooksRegistered = true;
    }

    /**
     * Register admin-bar hooks for operational controls in both admin and frontend toolbar contexts.
     *
     * @return void
     */
    public static function registerAdminBarHooks(): void
    {
        if (self::$adminBarHooksRegistered) {
            return;
        }

        $requiredClasses = [
            AdminBarMenu::class,
            AdminBarAction::class,
            Repository::class,
            Triggers::class,
        ];

        foreach ($requiredClasses as $requiredClass) {
            if (!class_exists($requiredClass, true)) {
                self::logBootstrapIssue(sprintf('admin bar module skipped: missing class %s', $requiredClass));
                return;
            }
        }

        add_action('wp_enqueue_scripts', [AdminBarMenu::class, 'enqueueFrontendStyles']);
        add_action('wp_before_admin_bar_render', [AdminBarMenu::class, 'registerMenuBeforeRender'], PHP_INT_MAX);

        add_action('admin_post_ekesto_cache_warmup_toggle_enabled', [AdminBarAction::class, 'handleToggleEnabled']);
        add_action('admin_post_ekesto_cache_warmup_run_full', [AdminBarAction::class, 'handleRunFullWarmup']);

        self::$adminBarHooksRegistered = true;
    }

    /**
     * Register REST hooks lazily so non-REST requests do not depend on settings controllers.
     *
     * @return void
     */
    public static function registerRestHooks(): void
    {
        if (self::$restHooksRegistered) {
            return;
        }

        if (class_exists(ImmediateKick::class, true)) {
            ImmediateKick::registerRestEndpoint();
        } else {
            self::logBootstrapIssue(sprintf('REST module skipped: missing class %s', ImmediateKick::class));
        }

        if (class_exists(RestController::class, true)) {
            RestController::registerRestEndpoints();
        } else {
            self::logBootstrapIssue(sprintf('REST module skipped: missing class %s', RestController::class));
        }

        self::$restHooksRegistered = true;
    }

    /**
     * Plugin base URL.
     *
     * @return string
     */
    public static function url(): string
    {
        return self::$baseUrl;
    }

    /**
     * Plugin base path.
     *
     * @return string
     */
    public static function path(): string
    {
        return self::$basePath;
    }

    /**
     * Handle manual queue reset from the settings screen.
     *
     * @return void
     */
    public static function handleResetQueue(): void
    {
        Debug::log('handleResetQueue', [
            'run_id' => (string) (Queue::get()['run_id'] ?? ''),
            'active' => !empty(Queue::get()['active']),
        ]);

        if (!Access::userCanManage()) {
            wp_die(esc_html__('You do not have permission to do this.', 'cache-autopilot'));
        }

        check_admin_referer('ekesto_cache_warmup_reset_queue');

        $state = Queue::get();
        $runId = isset($state['run_id']) ? (string) $state['run_id'] : '';
        if ($runId !== '' && !empty($state['active'])) {
            Logger::finishRun(
                $runId,
                'stopped',
                Queue::getTotalUrls($state),
                (int) ($state['warmed'] ?? 0),
                (int) ($state['failed'] ?? 0),
                __('Queue reset manually in settings.', 'cache-autopilot')
            );
        }

        Queue::clear();

        $baseUrl = AdminPage::settingsPageUrl([
            'tab' => 'logging',
            'ekesto_ecw_queue_reset' => '1',
        ]);

        wp_safe_redirect($baseUrl);
        exit;
    }

    /**
     * Emit a concise bootstrap warning without relying on optional runtime classes.
     *
     * @param string $message Human-readable warning message.
     * @return void
     */
    private static function logBootstrapIssue(string $message): void
    {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug-only bootstrap fallback when warmup services may not be fully initialized.
            error_log('Cache Warmup ' . $message);
        }
    }
}
