<?php
declare(strict_types=1);

namespace Instinctor\Core;

if (!defined('ABSPATH')) exit; // No direct access.

if ( ! defined( 'ABSPATH' ) && ( ! defined( 'INSTINCTOR_ENV' ) || INSTINCTOR_ENV !== 'standalone' ) ) exit;

/**
 * Asset registry — single source of truth for every JS/CSS handle the plugin
 * registers with WordPress's enqueue system. Templates call `Assets::enqueue()`
 * with a handle name; this class knows the file path, deps, and footer flag.
 *
 * Keeps the wp_enqueue surface clean (one place to add/rename a handle) and
 * satisfies WP.org Guideline 11 (use wp_enqueue_script/style instead of
 * inline <script src="..."> tags in templates).
 *
 * @since 4.2
 */
final class Assets
{
    /**
     * Handle → metadata. Each entry: ['type'=>'js'|'css', 'path'=>relative,
     * 'deps'=>[], 'footer'=>true].
     */
    /**
     * Handles whose script calls jQuery. Registered with no dependency
     * historically, which only worked while some other handle happened to load
     * jQuery first; conditional engine loading removed that accident.
     * WordPress core ships 'jquery', standalone ships 'instinctor-jquery'.
     */
    private const JQUERY_DEPENDENT = [
        'instinctor-frontend-js',
    ];

    private const REGISTRY = [
        // ── Vendor (standalone-only — WP core ships its own jquery) ─────
        'instinctor-jquery'            => ['type'=>'js',  'path'=>'assets/js/vendor/jquery.min.js',        'deps'=>[],                            'footer'=>false],
        'instinctor-jquery-ui'         => ['type'=>'js',  'path'=>'assets/js/vendor/jquery-ui.min.js',     'deps'=>['instinctor-jquery'],         'footer'=>false],
        'instinctor-html2canvas'       => ['type'=>'js',  'path'=>'assets/js/vendor/html2canvas.min.js',   'deps'=>[],                            'footer'=>true],

        // ── Frontend bundle ───────────────────────────────────────────────
        'instinctor-frontend-js'       => ['type'=>'js',  'path'=>'assets/js/frontend.js',                 'deps'=>[],                            'footer'=>true],
        'instinctor-chat'              => ['type'=>'js',  'path'=>'assets/js/instinctor-chat.js',          'deps'=>[],                            'footer'=>true],

        // ── Shared utilities ──────────────────────────────────────────────
        // Self-contained QR encoder (no external service). Used by the editor
        // "open on phone" popover, the Settings review-tool preview, and the
        // tenant control-panel QR generator.
        'instinctor-qr'                => ['type'=>'js',  'path'=>'assets/js/instinctor-qr.js',             'deps'=>[],                            'footer'=>false],

        // ── Admin (editor) ────────────────────────────────────────────────
        'instinctor-sortable'          => ['type'=>'js',  'path'=>'assets/js/vendor/sortable.min.js',      'deps'=>[],                            'footer'=>false],
        'instinctor-element-config'    => ['type'=>'js',  'path'=>'assets/js/impulse-element-config.js',   'deps'=>[],                            'footer'=>false],
        'instinctor-live-preview'      => ['type'=>'js',  'path'=>'assets/js/impulse-live-preview-v2.js',  'deps'=>[],                            'footer'=>false],
        'instinctor-lucide-icons'      => ['type'=>'js',  'path'=>'assets/js/impulse-lucide-icons.js',            'deps'=>[],                     'footer'=>true],
        'instinctor-admin-core'        => ['type'=>'js',  'path'=>'assets/js/admin/instinctor-admin-core.js',     'deps'=>['instinctor-lucide-icons'], 'footer'=>true],
        'instinctor-admin-timeline'    => ['type'=>'js',  'path'=>'assets/js/admin/instinctor-admin-timeline.js', 'deps'=>['instinctor-admin-core'], 'footer'=>true],
        'instinctor-admin-products'    => ['type'=>'js',  'path'=>'assets/js/admin/instinctor-admin-products.js', 'deps'=>['instinctor-admin-core'], 'footer'=>true],
        'instinctor-admin-chat'        => ['type'=>'js',  'path'=>'assets/js/admin/instinctor-admin-chat.js',     'deps'=>['instinctor-admin-core'], 'footer'=>true],
        'instinctor-admin-store'       => ['type'=>'js',  'path'=>'assets/js/admin/instinctor-admin-store.js',    'deps'=>['instinctor-admin-core'], 'footer'=>true],
        'instinctor-admin-media'       => ['type'=>'js',  'path'=>'assets/js/admin/instinctor-admin-media.js',    'deps'=>['instinctor-admin-core'], 'footer'=>true],
        'instinctor-admin-seo'         => ['type'=>'js',  'path'=>'assets/js/admin/instinctor-admin-seo.js',      'deps'=>['instinctor-admin-core'], 'footer'=>true],
        'instinctor-admin-configurator'=> ['type'=>'js',  'path'=>'assets/js/admin/instinctor-admin-configurator.js','deps'=>['instinctor-admin-core'], 'footer'=>true, 'optional'=>true],

        // ── Frontend renderers (impulse-page.php) ─────────────────────────
        // ALL graphics + engine scripts now footer:true (2026-05-30 — stage A
        // of the perf overhaul). Previously these all blocked first paint
        // because they were registered with footer:false → emitted inside
        // <head>, and the browser couldn't paint a pixel until every one had
        // downloaded + parsed (2-4s on mobile). End-of-body execution means
        // HTML parses first, the user sees content immediately, then the
        // engine + WebGL + 3D-text bootstrap behind the scenes. Dep chains
        // unchanged so WP still enqueues three before its dependents.
        'instinctor-three'             => ['type'=>'js',  'path'=>'assets/js/vendor/three.min.js',         'deps'=>[],                            'footer'=>true],
        'instinctor-motion-lava'       => ['type'=>'js',  'path'=>'assets/js/impulse-motion-lava.js',      'deps'=>[],                            'footer'=>true],
        'instinctor-wave'              => ['type'=>'js',  'path'=>'assets/js/impulse-wave.js',             'deps'=>[],                            'footer'=>true],
        'instinctor-3d-effects'        => ['type'=>'js',  'path'=>'assets/js/impulse-3d-effects.js',       'deps'=>['instinctor-three'],          'footer'=>true],
        'instinctor-fx-engine'         => ['type'=>'js',  'path'=>'assets/js/impulse-fx-engine.js',        'deps'=>[],                            'footer'=>true],
        'instinctor-gl-transitions'    => ['type'=>'js',  'path'=>'assets/js/impulse-gl-transitions.js',   'deps'=>[],                            'footer'=>true],
        'instinctor-timeline'          => ['type'=>'js',  'path'=>'assets/js/impulse-timeline.js',         'deps'=>[],                            'footer'=>true],
        'instinctor-screens'           => ['type'=>'js',  'path'=>'assets/js/impulse-screens.js',          'deps'=>[],                            'footer'=>true],
        'instinctor-depth-parallax'    => ['type'=>'js',  'path'=>'assets/js/impulse-depth-parallax.js',   'deps'=>['instinctor-three'],          'footer'=>true],
        'instinctor-depth-mesh'        => ['type'=>'js',  'path'=>'assets/js/impulse-depth-mesh.js',       'deps'=>['instinctor-three'],          'footer'=>true],
        'instinctor-autopilot'         => ['type'=>'js',  'path'=>'assets/js/impulse-autopilot.js',        'deps'=>[],                            'footer'=>true],
        'instinctor-crop-overlay'      => ['type'=>'js',  'path'=>'assets/js/impulse-crop-overlay.js',     'deps'=>[],                            'footer'=>true],
        'instinctor-bg-parallax'       => ['type'=>'js',  'path'=>'assets/js/impulse-bg-parallax.js',      'deps'=>[],                            'footer'=>true],
        'instinctor-feature-parallax'  => ['type'=>'js',  'path'=>'assets/js/impulse-feature-card-parallax.js', 'deps'=>[],                       'footer'=>true],
        'instinctor-icon-list-hover'   => ['type'=>'js',  'path'=>'assets/js/impulse-table-hover.js',  'deps'=>[],                            'footer'=>true],
        'instinctor-section-fx'        => ['type'=>'js',  'path'=>'assets/js/impulse-section-fx.js',       'deps'=>[],                            'footer'=>true],
        'instinctor-3d-text'           => ['type'=>'js',  'path'=>'assets/js/impulse-3d-text.js',          'deps'=>['instinctor-three'],          'footer'=>true],
        'instinctor-cell-depth'        => ['type'=>'js',  'path'=>'assets/js/impulse-cell-depth.js',       'deps'=>[],                            'footer'=>true],
        'instinctor-carousel-init'     => ['type'=>'js',  'path'=>'assets/js/impulse-carousel-init.js',    'deps'=>[],                            'footer'=>true],
        'instinctor-configurator'      => ['type'=>'js',  'path'=>'assets/js/impulse-configurator.js',     'deps'=>[],                            'footer'=>true, 'optional'=>true],
        'instinctor-frameloop'         => ['type'=>'js',  'path'=>'assets/js/impulse-frameloop.js',        'deps'=>[],                            'footer'=>true],
        'instinctor-animations'        => ['type'=>'js',  'path'=>'assets/js/impulse-animations.js',       'deps'=>[],                            'footer'=>true],
        'instinctor-interactions'      => ['type'=>'js',  'path'=>'assets/js/impulse-interactions.js',     'deps'=>[],                            'footer'=>true],
        'instinctor-heading-tilt'      => ['type'=>'js',  'path'=>'assets/js/impulse-title-el-tilt.js',     'deps'=>[],                            'footer'=>true],
        'instinctor-overlay-fx'        => ['type'=>'js',  'path'=>'assets/js/impulse-overlay-fx.js',       'deps'=>[],                            'footer'=>true],
        'instinctor-countdown'         => ['type'=>'js',  'path'=>'assets/js/impulse-countdown.js',        'deps'=>[],                            'footer'=>true],
        'instinctor-textwall'          => ['type'=>'js',  'path'=>'assets/js/impulse-textwall.js',         'deps'=>[],                            'footer'=>true],
        'instinctor-animator-js'       => ['type'=>'js',  'path'=>'assets/js/impulse-animator.js',          'deps'=>[],                            'footer'=>true],
        'instinctor-kinetic'           => ['type'=>'js',  'path'=>'assets/js/impulse-kinetic.js',           'deps'=>[],                            'footer'=>true],
        'instinctor-track'             => ['type'=>'js',  'path'=>'assets/js/impulse-track.js',             'deps'=>[],                            'footer'=>true],
        'instinctor-network'           => ['type'=>'js',  'path'=>'assets/js/impulse-network.js',           'deps'=>[],                            'footer'=>true],
        'instinctor-demo'              => ['type'=>'js',  'path'=>'assets/js/impulse-demo.js',              'deps'=>[],                            'footer'=>true],

        // ── Frontend styles ───────────────────────────────────────────────
        'instinctor-frontend-critical' => ['type'=>'css', 'path'=>'assets/css/frontend-critical.css',      'deps'=>[],                            'footer'=>false],
        'instinctor-frontend'          => ['type'=>'css', 'path'=>'assets/css/frontend.css',               'deps'=>['instinctor-frontend-critical'], 'footer'=>false],
        'instinctor-frontend-addons'   => ['type'=>'css', 'path'=>'assets/css/frontend-addons.css',        'deps'=>['instinctor-frontend'],       'footer'=>false],
        'instinctor-chat-widget'       => ['type'=>'css', 'path'=>'assets/css/chat-widget.css',            'deps'=>[],                            'footer'=>false],
        // Per-block stylesheets (singleton-enqueued on first use by their renderer).
        'instinctor-mesh'              => ['type'=>'css', 'path'=>'assets/css/impulse-mesh.css',            'deps'=>[],                            'footer'=>false],
        'instinctor-unfold'            => ['type'=>'css', 'path'=>'assets/css/impulse-unfold.css',          'deps'=>[],                            'footer'=>false],
        'instinctor-chart'             => ['type'=>'css', 'path'=>'assets/css/impulse-chart.css',           'deps'=>[],                            'footer'=>false],
        'instinctor-animator'          => ['type'=>'css', 'path'=>'assets/css/impulse-animator.css',        'deps'=>[],                            'footer'=>false],
        'instinctor-network-css'       => ['type'=>'css', 'path'=>'assets/css/impulse-network.css',         'deps'=>[],                            'footer'=>false],
        'instinctor-marquee-css'       => ['type'=>'css', 'path'=>'assets/css/impulse-marquee.css',         'deps'=>[],                            'footer'=>false],
        // Inline-only handle for per-page computed CSS (no src). Every dynamic
        // `<style>` block in the renderer routes through wp_add_inline_style()
        // against this handle so output complies with WP.org's enqueue rule.
        // Moved to HEAD (footer:false) on 2026-05-30. Was footer:true on the
        // assumption that wp_head() had already fired by render time, but
        // standalone bypasses wp_head() entirely; in WP context the handle
        // emits during wp_footer regardless. Either way, footer:true meant
        // every per-element grid layout, section bg, chrome-hide, and
        // responsive @media rule landed AFTER first paint and caused visible
        // re-layout. Promoting to head: emits the same CSS but BEFORE paint,
        // so the page paints with the correct layout once. Critical chunk
        // (html bg + chrome-hide + animated-default) still lives in the
        // <style id="impulse-critical-css"> block directly in <head> for the
        // absolute earliest-paint state.
        'instinctor-page-inline'       => ['type'=>'css', 'path'=>'',                                      'deps'=>['instinctor-frontend'],       'footer'=>false],
    ];

    /**
     * Register all known handles with WordPress (or our standalone shim).
     * Idempotent — safe to call multiple times.
     */
    public static function registerAll(): void
    {
        $base = defined('INSTINCTOR_PLUGIN_URL') ? rtrim(INSTINCTOR_PLUGIN_URL, '/') . '/' : '';
        $ver  = defined('INSTINCTOR_VERSION')    ? INSTINCTOR_VERSION : '0';
        foreach (self::REGISTRY as $handle => $meta) {
            // Optional add-ons (e.g. the configurator) are NEVER shipped in the
            // plugin zips — they're installed manually as a companion. Skip
            // registering one when its file is absent, so the editor doesn't
            // enqueue a 404. When the add-on is dropped in, the file exists and
            // it registers normally.
            if (!empty($meta['optional']) && $meta['path'] !== ''
                && defined('INSTINCTOR_PLUGIN_DIR')
                && !file_exists(INSTINCTOR_PLUGIN_DIR . $meta['path'])) {
                continue;
            }
            // Empty path = inline-only handle (no link/script tag, only used as
            // a wp_add_inline_style/script accumulator). Pass false as src so
            // WordPress doesn't try to print an empty href/src.
            $src = $meta['path'] === '' ? false : $base . $meta['path'];
            $deps = $meta['deps'];
            if (in_array($handle, self::JQUERY_DEPENDENT, true)) {
                $deps[] = (defined('INSTINCTOR_ENV') && INSTINCTOR_ENV === 'standalone')
                    ? 'instinctor-jquery'
                    : 'jquery';
            }
            if ($meta['type'] === 'js') {
                wp_register_script($handle, $src, $deps, $ver, $meta['footer']);
            } else {
                wp_register_style($handle, $src, $deps, $ver);
            }
        }
    }

    /**
     * Mark a handle as needed for the current request. Handle must be in the
     * registry. Calls wp_enqueue_script/style under the hood.
     */
    public static function enqueue(string $handle): void
    {
        if (!isset(self::REGISTRY[$handle])) {
            // Fail loud in dev so misspelled handles surface fast.
            if (defined('INSTINCTOR_DEBUG') && INSTINCTOR_DEBUG) {
                // phpcs:disable WordPress.PHP.DevelopmentFunctions -- server-side error capture / one-time migration logging, not debug output.
                trigger_error('Instinctor\\Core\\Assets::enqueue: unknown handle "' . esc_html($handle) . '"', E_USER_WARNING);
                // phpcs:enable WordPress.PHP.DevelopmentFunctions
            }
            return;
        }
        $meta = self::REGISTRY[$handle];
        if ($meta['type'] === 'js') {
            wp_enqueue_script($handle);
        } else {
            wp_enqueue_style($handle);
        }
    }

    /**
     * Bulk enqueue convenience: pass an array of handles.
     */
    public static function enqueueMany(array $handles): void
    {
        foreach ($handles as $h) self::enqueue($h);
    }
}
