<?php

namespace Calio\Core;

use Calio\Support\Settings;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Elementor asset manager aligned with Element Pack hook flow using Calio namespace only.
 */
class Assets
{
    private const HANDLE_GLOBAL_STYLES = 'calio-styles';
    private const HANDLE_GLOBAL_SCRIPTS = 'calio-scripts';
    private const HANDLE_PREVIEW_STYLE = 'calio-preview';
    private const HANDLE_HELPER = 'calio-helper';
    private const HANDLE_UIKIT = 'calio-uikit';

    private bool $adminAssetsRegistered = false;
    private bool $assetsRegistered = false;

    /**
     * Maps Calio extension asset handles to their build directory slug.
     *
     * @var array<string, string>
     */
    private array $extensionAssetMap = [
        'calio-extension-glass-filter' => 'glass-filter',
        'calio-extension-reveal-parallax' => 'reveal-parallax',
        'calio-extension-scroll-fill-effect' => 'scroll-fill-effect',
        'calio-extension-sticky-content' => 'sticky-content',
        'calio-extension-wrapper-link' => 'wrapper-link',
    ];

    /**
     * Maps Calio widget handles to their build directory slug.
     *
     * @var array<string, string>
     */
    private array $widgetStyleMap = [
        'calio-widget-accent-headline' => 'accent-headline',
        'calio-widget-accordion' => 'accordion',
        'calio-widget-advanced-heading' => 'advanced-heading',
        'calio-widget-advanced-button' => 'advanced-button',
        'calio-widget-animated-link' => 'animated-link',
        'calio-widget-business-hours' => 'business-hours',
        'calio-widget-breadcrumbs' => 'breadcrumbs',
        'calio-widget-call-out' => 'call-out',
        'calio-widget-cube-slider' => 'cube-slider',
        'calio-widget-fancy-chart' => 'fancy-chart',
        'calio-widget-fancy-tabs' => 'fancy-tabs',
        'calio-widget-feature-list' => 'feature-list',
        'calio-widget-feature-list-simple' => 'feature-list-simple',
        'calio-widget-filterable-gallery' => 'filterable-gallery',
        'calio-widget-image-flow' => 'image-flow',
        'calio-widget-image-gallery' => 'image-gallery',
        'calio-widget-image-comparison-slider' => 'image-comparison-slider',
        'calio-widget-countdown' => 'countdown',
        'calio-widget-post-grid' => 'post-grid',
        'calio-widget-post-carousel' => 'post-carousel',
        'calio-widget-post-timeline' => 'post-timeline',
        'calio-widget-multicolumn-pricing-table' => 'multicolumn-pricing-table',
        'calio-widget-navbar' => 'navbar',
        'calio-widget-mega-menu' => 'mega-menu',
        'calio-widget-posts-tab' => 'posts-tab',
        'calio-widget-team-member' => 'team-member',
        'calio-widget-team-slider' => 'team-slider',
        'calio-widget-woocommerce-slider' => 'woocommerce-slider',
        'calio-widget-stylish-list' => 'stylish-list',
        'calio-widget-table' => 'table',
        'calio-widget-kinetic-card' => 'kinetic-card',
        'calio-widget-progress-pie' => 'progress-pie',
    ];

    /**
     * Script map intentionally mirrors style map so widget dependency contracts are uniform.
     *
     * @var array<string, string>
     */
    private array $widgetScriptMap = [];

    /**
     * Per-widget script dependencies.
     *
     * @var array<string, array<int, string>>
     */
    private array $widgetScriptDeps = [
        'calio-widget-accordion' => ['jquery'],
        'calio-widget-cube-slider' => ['swiper'],
        'calio-widget-team-slider' => ['swiper'],
        'calio-widget-woocommerce-slider' => ['swiper'],
    ];

    public function __construct()
    {
        $this->widgetScriptMap = $this->widgetStyleMap;

        add_action('init', [$this, 'register_admin_assets']);

        // Element Pack-aligned registration/enqueue flow.
        add_action('elementor/frontend/before_register_styles', [$this, 'register_site_styles']);
        add_action('elementor/frontend/before_register_scripts', [$this, 'register_site_scripts']);
        add_action('elementor/preview/enqueue_styles', [$this, 'enqueue_preview_styles']);
        add_action('elementor/frontend/after_register_styles', [$this, 'enqueue_site_styles']);
        add_action('elementor/frontend/before_enqueue_scripts', [$this, 'enqueue_site_scripts']);
        add_action('elementor/frontend/after_enqueue_styles', [$this, 'enqueue_minified_css']);
        add_action('elementor/frontend/after_enqueue_scripts', [$this, 'enqueue_minified_js']);

        // Additional preview script hook to make iframe behavior deterministic.
        add_action('elementor/preview/enqueue_scripts', [$this, 'enqueue_preview_scripts']);

        // Fallback when third-party optimizers alter Elementor hook order.
        add_action('wp_enqueue_scripts', [$this, 'enqueue_preview_fallback_assets'], 999);
        add_action('wp_print_styles', [$this, 'enqueue_preview_fallback_assets'], 1);
        add_action('wp_print_scripts', [$this, 'enqueue_preview_fallback_assets'], 1);
    }

    /**
     * Registers dashboard assets from the generated build directory.
     */
    public function register_admin_assets(): void
    {
        if ($this->adminAssetsRegistered) {
            return;
        }

        $rtl = is_rtl();

        $styleAsset = $this->resolve_asset([
            $rtl ? 'assets/build/admin/style-index-rtl.css' : 'assets/build/admin/style-index.css',
            'assets/build/admin/index.css',
        ]);

        $scriptAsset = $this->resolve_asset([
            'assets/build/admin/index.js',
        ]);

        if ($styleAsset !== null) {
            wp_register_style('calio-admin-ui', $styleAsset['url'], [], $styleAsset['version']);
        }

        if ($scriptAsset !== null) {
            $assetMeta = $this->resolve_script_asset_meta('assets/build/admin/index.asset.php', ['jquery']);
            wp_register_script('calio-admin-ui', $scriptAsset['url'], $assetMeta['deps'], $assetMeta['version'] ?: $scriptAsset['version'], true);
        }

        $this->adminAssetsRegistered = true;
    }

    public function register_site_styles(): void
    {
        $this->register_assets();
    }

    public function register_site_scripts(): void
    {
        $this->register_assets();
    }

    public function enqueue_site_styles(): void
    {
        $this->register_assets();

        if ($this->should_load_assets_globally()) {
            wp_enqueue_style(self::HANDLE_UIKIT);
            wp_enqueue_style(self::HANDLE_HELPER);
            wp_enqueue_style(self::HANDLE_GLOBAL_STYLES);
        }

        $this->enqueue_enabled_extension_styles();

        if ($this->should_load_assets_globally()) {
            $this->enqueue_all_widget_styles();
        }
    }

    public function enqueue_site_scripts(): void
    {
        $this->register_assets();

        if ($this->should_load_assets_globally()) {
            wp_enqueue_script(self::HANDLE_UIKIT);
            wp_enqueue_script(self::HANDLE_HELPER);
            wp_enqueue_script(self::HANDLE_GLOBAL_SCRIPTS);
        }

        $this->enqueue_enabled_extension_scripts();

        if ($this->should_load_assets_globally()) {
            $this->enqueue_all_widget_scripts();
        }
    }

    public function enqueue_preview_styles(): void
    {
        $this->register_assets();

        wp_enqueue_style(self::HANDLE_PREVIEW_STYLE);
        wp_enqueue_style(self::HANDLE_GLOBAL_STYLES);

        $this->enqueue_all_widget_styles();
        $this->enqueue_enabled_extension_styles();
    }

    public function enqueue_preview_scripts(): void
    {
        $this->register_assets();

        wp_enqueue_script(self::HANDLE_UIKIT);
        wp_enqueue_script(self::HANDLE_HELPER);
        wp_enqueue_script(self::HANDLE_GLOBAL_SCRIPTS);

        $this->enqueue_all_widget_scripts();
        $this->enqueue_enabled_extension_scripts();
    }

    /**
     * Global CSS entrypoint enqueued after frontend styles are prepared.
     */
    public function enqueue_minified_css(): void
    {
        $this->register_assets();

        if ($this->is_elementor_preview_context() || $this->should_load_assets_globally()) {
            wp_enqueue_style(self::HANDLE_GLOBAL_STYLES);
        }
    }

    /**
     * Global JS entrypoint enqueued after frontend scripts are prepared.
     */
    public function enqueue_minified_js(): void
    {
        $this->register_assets();

        if ($this->is_elementor_preview_context() || $this->should_load_assets_globally()) {
            wp_enqueue_script(self::HANDLE_GLOBAL_SCRIPTS);
        }
    }

    /**
     * Fallback loader for Elementor preview iframe requests.
     */
    public function enqueue_preview_fallback_assets(): void
    {
        if (!$this->is_elementor_preview_context()) {
            return;
        }

        $this->enqueue_preview_styles();
        $this->enqueue_preview_scripts();
    }

    /**
     * Registers all widget/extension/frontend assets.
     */
    private function register_assets(): void
    {
        if ($this->assetsRegistered) {
            return;
        }

        $this->register_swiper_fallback();
        $this->register_global_handles();
        $this->register_widget_assets();
        $this->register_extension_assets();

        $this->assetsRegistered = true;
    }

    private function register_swiper_fallback(): void
    {
        if (!wp_script_is('swiper', 'registered')) {
            $scriptPath = WP_PLUGIN_DIR . '/elementor/assets/lib/swiper/v8/swiper.min.js';
            if (is_readable($scriptPath)) {
                wp_register_script(
                    'swiper',
                    plugins_url('elementor/assets/lib/swiper/v8/swiper.min.js'),
                    [],
                    '8.4.5',
                    true
                );
            }
        }

        if (!wp_style_is('swiper', 'registered')) {
            $stylePath = WP_PLUGIN_DIR . '/elementor/assets/lib/swiper/v8/css/swiper.min.css';
            if (is_readable($stylePath)) {
                wp_register_style(
                    'swiper',
                    plugins_url('elementor/assets/lib/swiper/v8/css/swiper.min.css'),
                    [],
                    '8.4.5'
                );
            }
        }
    }

    private function register_global_handles(): void
    {
        $rtl = is_rtl();

        $globalStyle = $this->resolve_asset([
            $rtl ? 'build/frontend/style-index-rtl.css' : 'build/frontend/style-index.css',
            'build/frontend/index.css',
        ]);

        $globalScript = $this->resolve_asset([
            'build/frontend/index.js',
        ]);

        $editorStyle = $this->resolve_asset([
            $rtl ? 'assets/build/admin/style-index-rtl.css' : 'assets/build/admin/style-index.css',
            'assets/build/admin/index.css',
        ]);

        $this->register_style_or_fallback(self::HANDLE_UIKIT, null, []);
        $this->register_script_or_fallback(self::HANDLE_UIKIT, null, ['jquery']);

        $shouldAttachGlobalBundle = !$this->is_live_frontend_request() || $this->should_load_assets_globally();

        $this->register_style_or_fallback(self::HANDLE_HELPER, $shouldAttachGlobalBundle ? $globalStyle : null, [self::HANDLE_UIKIT]);
        $this->register_script_or_fallback(self::HANDLE_HELPER, null, ['jquery', self::HANDLE_UIKIT]);

        $globalMeta = $this->resolve_script_asset_meta('build/frontend/index.asset.php', ['jquery', self::HANDLE_UIKIT, self::HANDLE_HELPER]);

        $this->register_style_or_fallback(
            self::HANDLE_GLOBAL_STYLES,
            $shouldAttachGlobalBundle ? $globalStyle : null,
            [self::HANDLE_UIKIT, self::HANDLE_HELPER]
        );
        $this->register_script_or_fallback(
            self::HANDLE_GLOBAL_SCRIPTS,
            $shouldAttachGlobalBundle ? $globalScript : null,
            $globalMeta['deps'],
            $globalMeta['version']
        );

        $previewStyle = $globalStyle ?? $editorStyle;
        $this->register_style_or_fallback(self::HANDLE_PREVIEW_STYLE, $previewStyle, [self::HANDLE_GLOBAL_STYLES]);
    }

    private function register_widget_assets(): void
    {
        $rtl = is_rtl();

        foreach ($this->widgetStyleMap as $handle => $slug) {
            $styleAsset = $this->resolve_asset([
                $rtl ? "assets/build/widgets/{$slug}/style-index-rtl.css" : "assets/build/widgets/{$slug}/style-index.css",
                "assets/build/widgets/{$slug}/index.css",
            ]);

            if ($styleAsset !== null) {
                wp_register_style($handle, $styleAsset['url'], [self::HANDLE_GLOBAL_STYLES], $styleAsset['version']);
            }
        }

        foreach ($this->widgetScriptMap as $handle => $slug) {
            $scriptAsset = $this->resolve_asset([
                "assets/build/widgets/{$slug}/index.js",
            ]);

            if ($scriptAsset !== null) {
                $fallbackDeps = array_merge([self::HANDLE_GLOBAL_SCRIPTS], $this->widgetScriptDeps[$handle] ?? []);
                $assetMeta = $this->resolve_script_asset_meta("assets/build/widgets/{$slug}/index.asset.php", $fallbackDeps);

                wp_register_script(
                    $handle,
                    $scriptAsset['url'],
                    $assetMeta['deps'],
                    $assetMeta['version'] ?: $scriptAsset['version'],
                    true
                );
            }
        }
    }

    private function register_extension_assets(): void
    {
        $rtl = is_rtl();

        foreach ($this->extensionAssetMap as $handle => $slug) {
            $styleAsset = $this->resolve_asset([
                $rtl ? "assets/build/extensions/{$slug}/style-index-rtl.css" : "assets/build/extensions/{$slug}/style-index.css",
                "assets/build/extensions/{$slug}/index.css",
            ]);

            $scriptAsset = $this->resolve_asset([
                "assets/build/extensions/{$slug}/index.js",
            ]);

            if ($styleAsset !== null) {
                wp_register_style($handle, $styleAsset['url'], [self::HANDLE_GLOBAL_STYLES], $styleAsset['version']);
            }

            if ($scriptAsset !== null) {
                $assetMeta = $this->resolve_script_asset_meta(
                    "assets/build/extensions/{$slug}/index.asset.php",
                    [self::HANDLE_GLOBAL_SCRIPTS, 'jquery']
                );
                wp_register_script($handle, $scriptAsset['url'], $assetMeta['deps'], $assetMeta['version'] ?: $scriptAsset['version'], true);
            }
        }
    }

    private function enqueue_enabled_extension_styles(): void
    {
        if (Settings::is_extension_enabled('glass_filter')) {
            wp_enqueue_style('calio-extension-glass-filter');
        }

        if (Settings::is_extension_enabled('reveal_parallax')) {
            wp_enqueue_style('calio-extension-reveal-parallax');
        }

        if (Settings::is_extension_enabled('scroll_fill_effect')) {
            wp_enqueue_style('calio-extension-scroll-fill-effect');
        }

        if (Settings::is_extension_enabled('sticky_content')) {
            wp_enqueue_style('calio-extension-sticky-content');
        }

        if (Settings::is_extension_enabled('wrapper_link')) {
            wp_enqueue_style('calio-extension-wrapper-link');
        }
    }

    private function enqueue_enabled_extension_scripts(): void
    {
        if (Settings::is_extension_enabled('glass_filter')) {
            wp_enqueue_script('calio-extension-glass-filter');
        }

        if (Settings::is_extension_enabled('reveal_parallax')) {
            wp_enqueue_script('calio-extension-reveal-parallax');
        }

        if (Settings::is_extension_enabled('scroll_fill_effect')) {
            wp_enqueue_script('calio-extension-scroll-fill-effect');
        }

        if (Settings::is_extension_enabled('sticky_content')) {
            wp_enqueue_script('calio-extension-sticky-content');
        }

        if (Settings::is_extension_enabled('wrapper_link')) {
            wp_enqueue_script('calio-extension-wrapper-link');
        }
    }

    private function enqueue_all_widget_styles(): void
    {
        foreach (array_keys($this->widgetStyleMap) as $handle) {
            if (wp_style_is($handle, 'registered')) {
                wp_enqueue_style($handle);
            }
        }
    }

    private function enqueue_all_widget_scripts(): void
    {
        foreach (array_keys($this->widgetScriptMap) as $handle) {
            if (wp_script_is($handle, 'registered')) {
                wp_enqueue_script($handle);
            }
        }
    }

    /**
     * Resolves the first readable asset candidate into a public plugin URL.
     *
     * @param array<int, string> $relativeCandidates Candidate paths relative to the plugin root.
     */
    private function resolve_asset(array $relativeCandidates): ?array
    {
        foreach ($relativeCandidates as $candidate) {
            $path = CALIO_PATH . $candidate;
            if (is_readable($path)) {
                $version = (string) (filemtime($path) ?: CALIO_VERSION);
                return [
                    'url' => CALIO_URL . str_replace('\\', '/', $candidate),
                    'version' => $version,
                ];
            }
        }

        return null;
    }

    private function register_style_or_fallback(string $handle, ?array $asset, array $deps = []): void
    {
        if (wp_style_is($handle, 'registered')) {
            return;
        }

        if ($asset !== null) {
            wp_register_style($handle, $asset['url'], $deps, $asset['version']);
            return;
        }

        wp_register_style($handle, false, $deps, CALIO_VERSION);
    }

    private function register_script_or_fallback(string $handle, ?array $asset, array $deps = [], string $version = ''): void
    {
        if (wp_script_is($handle, 'registered')) {
            return;
        }

        $resolvedVersion = $version !== '' ? $version : CALIO_VERSION;

        if ($asset !== null) {
            wp_register_script($handle, $asset['url'], $deps, $resolvedVersion ?: $asset['version'], true);
            return;
        }

        wp_register_script($handle, false, $deps, $resolvedVersion, true);
    }

    /**
     * @return array{deps: array<int, string>, version: string}
     */
    private function resolve_script_asset_meta(string $relativeAssetMetaPath, array $fallbackDeps): array
    {
        $deps = $fallbackDeps;
        $version = CALIO_VERSION;

        $assetMetaPath = CALIO_PATH . $relativeAssetMetaPath;
        if (!is_readable($assetMetaPath)) {
            if (!in_array('jquery', $deps, true)) {
                $deps[] = 'jquery';
            }

            return [
                'deps' => array_values(array_unique($deps)),
                'version' => (string) $version,
            ];
        }

        $metadata = require $assetMetaPath;
        if (is_array($metadata)) {
            if (!empty($metadata['dependencies']) && is_array($metadata['dependencies'])) {
                $deps = array_merge($deps, array_map('strval', $metadata['dependencies']));
            }

            if (!empty($metadata['version']) && is_string($metadata['version'])) {
                $version = $metadata['version'];
            }
        }

        if (!in_array('jquery', $deps, true)) {
            $deps[] = 'jquery';
        }

        return [
            'deps' => array_values(array_unique($deps)),
            'version' => (string) $version,
        ];
    }

    private function should_load_assets_globally(): bool
    {
        $otherSettings = Settings::other_settings();
        $enabled = !empty($otherSettings['load_assets_globally']);

        // Default runtime is always conditional on live frontend for performance parity with Element Pack.
        if ($this->is_live_frontend_request()) {
            $enabled = false;
        }

        /**
         * Allows advanced installs to force global loading on frontend when absolutely needed.
         *
         * @param bool $enabled Whether global mode is active.
         */
        return (bool) apply_filters('calio/force_global_assets_mode', $enabled);
    }

    private function is_live_frontend_request(): bool
    {
        return !is_admin() && !$this->is_elementor_preview_context();
    }

    private function is_elementor_preview_context(): bool
    {
        // Read-only request context detection; these values do not trigger state changes.
        // phpcs:disable WordPress.Security.NonceVerification.Recommended
        if (isset($_GET['elementor-preview']) || isset($_GET['elementor-preview-iframe'])) {
            return true;
        }

        if (isset($_GET['preview_id']) && isset($_GET['preview_nonce'])) {
            return true;
        }

        if (isset($_GET['action']) && sanitize_key((string) wp_unslash($_GET['action'])) === 'elementor') {
            return true;
        }
        // phpcs:enable WordPress.Security.NonceVerification.Recommended

        $referer = isset($_SERVER['HTTP_REFERER']) ? esc_url_raw((string) wp_unslash($_SERVER['HTTP_REFERER'])) : '';
        if ($referer !== '' && (strpos($referer, 'elementor-preview=') !== false || strpos($referer, 'action=elementor') !== false)) {
            return true;
        }

        if (!class_exists('\\Elementor\\Plugin')) {
            return false;
        }

        $plugin = \Elementor\Plugin::$instance ?? null;
        if (!is_object($plugin)) {
            return false;
        }

        $isPreview = isset($plugin->preview) && method_exists($plugin->preview, 'is_preview_mode') && $plugin->preview->is_preview_mode();
        $isEditor = isset($plugin->editor) && method_exists($plugin->editor, 'is_edit_mode') && $plugin->editor->is_edit_mode();

        return $isPreview || $isEditor;
    }
}
