<?php

namespace PluginsNameSpaces\Hooks\Handlers;

use PluginsNameSpaces\App;

/**
 * Enqueues the Vue/Vite SPA on the plugin's admin pages.
 *
 * Dev mode (a ".hot" file written by `npm run dev`) loads the entry from
 * the Vite dev server with HMR. Production reads the Vite manifest at
 * public/.vite/manifest.json and enqueues the hashed JS + CSS, giving
 * content-hash cache busting. A legacy fixed-filename path is kept as a
 * fallback when no manifest is present.
 */
class AssetHandler
{
    /**
     * Vite dev server origin (must match vite.config.js `server.origin`).
     */
    const DEV_SERVER = 'http://localhost:5174';

    /**
     * The Vite entry, relative to the Vite root (resources/).
     */
    const ENTRY = 'js/bootstrap/app.js';

    /**
     * Script handle for the app bundle.
     */
    const HANDLE = '__PLUGIN_NAME__-app';

    /**
     * Hook into WordPress.
     *
     * @return void
     */
    public function register()
    {
        add_action('admin_enqueue_scripts', [$this, 'enqueue']);
        add_filter('script_loader_tag', [$this, 'asModule'], 10, 2);
    }

    /**
     * Conditionally enqueue assets on plugin pages only.
     *
     * @param string $hook Current admin page hook suffix.
     * @return void
     */
    public function enqueue($hook)
    {
        $slug = App::config('menu.slug');

        if (strpos($hook, $slug) === false) {
            return;
        }

        if (file_exists(__CONST_PREFIX___PATH . '.hot')) {
            $this->enqueueDev();
        } else {
            $this->enqueueProduction();
        }

        wp_localize_script(self::HANDLE, '__PLUGIN_GLOBAL__', [
            'restUrl' => esc_url_raw(rest_url(App::config('app.rest_namespace') . '/' . App::config('app.rest_version'))),
            'nonce'   => wp_create_nonce('wp_rest'),
            'ajaxUrl' => admin_url('admin-ajax.php'),
        ]);
    }

    /**
     * Load the entry straight from the Vite dev server (HMR).
     *
     * @return void
     */
    protected function enqueueDev()
    {
        wp_enqueue_script('__PLUGIN_NAME__-vite-client', self::DEV_SERVER . '/@vite/client', [], null, true);
        wp_enqueue_script(self::HANDLE, self::DEV_SERVER . '/' . self::ENTRY, [], null, true);
    }

    /**
     * Enqueue compiled assets, resolving hashed filenames from the Vite
     * manifest when available.
     *
     * @return void
     */
    protected function enqueueProduction()
    {
        $entry = $this->manifestEntry();

        if ($entry && ! empty($entry['file'])) {
            wp_enqueue_script(
                self::HANDLE,
                __CONST_PREFIX___URL . 'public/' . $entry['file'],
                [],
                __CONST_PREFIX___VERSION,
                true
            );

            foreach ($entry['css'] ?? [] as $i => $cssFile) {
                wp_enqueue_style(
                    self::HANDLE . ($i ? '-' . $i : ''),
                    __CONST_PREFIX___URL . 'public/' . $cssFile,
                    [],
                    __CONST_PREFIX___VERSION
                );
            }

            return;
        }

        // Fallback: fixed filenames (no manifest present).
        $js  = __CONST_PREFIX___BUILD_PATH . '/main.js';
        $css = __CONST_PREFIX___BUILD_PATH . '/main.css';

        wp_enqueue_script(
            self::HANDLE,
            __CONST_PREFIX___BUILD_URL . '/main.js',
            [],
            file_exists($js) ? filemtime($js) : __CONST_PREFIX___VERSION,
            true
        );

        if (file_exists($css)) {
            wp_enqueue_style(
                self::HANDLE,
                __CONST_PREFIX___BUILD_URL . '/main.css',
                [],
                filemtime($css)
            );
        }
    }

    /**
     * Read the entry record from the Vite manifest.
     *
     * @return array|null
     */
    protected function manifestEntry()
    {
        $path = __CONST_PREFIX___PATH . 'public/.vite/manifest.json';

        if (! file_exists($path)) {
            return null;
        }

        $manifest = json_decode((string) file_get_contents($path), true);

        if (! is_array($manifest)) {
            return null;
        }

        // Prefer the explicit entry key; otherwise fall back to the first
        // record flagged isEntry (single-entry build).
        if (isset($manifest[self::ENTRY])) {
            return $manifest[self::ENTRY];
        }

        foreach ($manifest as $record) {
            if (! empty($record['isEntry'])) {
                return $record;
            }
        }

        return null;
    }

    /**
     * Add type="module" to our scripts so ES modules load correctly.
     *
     * @param string $tag
     * @param string $handle
     * @return string
     */
    public function asModule($tag, $handle)
    {
        $handles = ['__PLUGIN_NAME__-vite-client', self::HANDLE];

        if (in_array($handle, $handles, true)) {
            $tag = str_replace('<script ', '<script type="module" ', $tag);
        }

        return $tag;
    }
}
