<?php

/**
 * Assets coordinator.
 *
 * @package B3WVS\VariationSwatches
 */
namespace B3WVS\VariationSwatches;

use B3WVS\VariationSwatches\Assets\Enqueuer;
use B3WVS\VariationSwatches\Assets\Localizer;
use B3WVS\VariationSwatches\Assets\ManifestReader;
use B3WVS\VariationSwatches\Assets\TypographyRenderer;
defined( 'ABSPATH' ) || exit;
/**
 * Assets Class
 */
class Assets {
    /**
     * Plugin directory path.
     *
     * @var string
     */
    private $plugin_path;

    /**
     * Plugin directory URL.
     *
     * @var string
     */
    private $plugin_url;

    /**
     * Instance of this class.
     *
     * @var Assets
     */
    private static $instance = null;

    /**
     * Manifest reader.
     *
     * @var ManifestReader
     */
    private $manifest_reader;

    /**
     * Typography renderer.
     *
     * @var TypographyRenderer
     */
    private $typography_renderer;

    /**
     * Localizer.
     *
     * @var Localizer
     */
    private $localizer;

    /**
     * Enqueuer.
     *
     * @var Enqueuer
     */
    private $enqueuer;

    /**
     * Get instance of this class.
     *
     * @return Assets
     */
    public static function get_instance() {
        if ( is_null( self::$instance ) ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Constructor.
     */
    private function __construct() {
        $this->plugin_path = plugin_dir_path( __DIR__ );
        $this->plugin_url = plugin_dir_url( __DIR__ );
        $this->manifest_reader = new ManifestReader($this->plugin_path);
        $this->typography_renderer = new TypographyRenderer();
        $this->localizer = new Localizer();
        $this->enqueuer = new Enqueuer(
            $this->plugin_path,
            $this->plugin_url,
            $this->manifest_reader,
            $this->localizer
        );
        add_action( 'wp_enqueue_scripts', array($this, 'enqueue_frontend_assets') );
        add_action( 'admin_enqueue_scripts', array($this, 'enqueue_admin_assets') );
    }

    /**
     * Get the enqueuer instance.
     *
     * @return Enqueuer
     */
    public function get_enqueuer() {
        return $this->enqueuer;
    }

    /**
     * Enqueue frontend assets.
     */
    public function enqueue_frontend_assets() {
        $should_enqueue = is_product();
        $has_extension_access = false;
        $frontend_asset_handle = 'frontend';
        // Archive-context loading is delegated to extension hooks so the shared
        // path stays limited to the base request checks.
        if ( !$should_enqueue ) {
            $should_enqueue = (bool) apply_filters( 'b3wvs_should_enqueue_extension_frontend_request', $should_enqueue );
        }
        // Page builder previews often render product forms on non-product URLs.
        if ( !$should_enqueue && $this->is_builder_preview() ) {
            $should_enqueue = true;
        }
        // Pages can render variable product forms via shortcodes (builders/templates), even when not a product.
        if ( !$should_enqueue && $this->current_post_has_woocommerce_shortcodes() ) {
            $should_enqueue = true;
        }
        // Pages can render products via WooCommerce Gutenberg blocks.
        if ( !$should_enqueue && $this->current_post_has_woocommerce_blocks() ) {
            $should_enqueue = true;
        }
        // Sidebars / widget areas may contain WooCommerce product widgets.
        if ( !$should_enqueue && $this->active_sidebars_have_product_widgets() ) {
            $should_enqueue = true;
        }
        /**
         * Filter whether frontend assets should be enqueued.
         *
         * Useful for page builders and custom templates.
         *
         * @param bool $should_enqueue Whether to enqueue.
         */
        $should_enqueue = (bool) apply_filters( 'b3wvs_should_enqueue_frontend_assets', $should_enqueue );
        if ( !$should_enqueue ) {
            return;
        }
        if ( !$has_extension_access ) {
            $this->enqueuer->enqueue_asset( 'frontend', 'src/frontend/main.tsx', array('wp-element') );
        }
        // Register a lightweight inline script to mark JS as enabled before swatches mount.
        $script_version = ( defined( 'B3_WVS_VERSION' ) ? B3_WVS_VERSION : false );
        wp_register_script(
            'b3-wvs-js-flag',
            false,
            array(),
            $script_version,
            false
        );
        wp_enqueue_script( 'b3-wvs-js-flag' );
        wp_add_inline_script( 'b3-wvs-js-flag', 'document.documentElement.classList.add("b3-wvs-js");' );
        $style_handle = $this->resolve_inline_style_handle( $frontend_asset_handle, 'b3-wvs-inline-frontend' );
        $this->enqueue_typography_inline_css(
            $style_handle,
            '',
            false,
            'frontend'
        );
    }

    /**
     * Detect page builder preview / edit requests.
     *
     * Many page builders render product forms inside preview iframes on non-product URLs.
     * This method checks for known query-string flags used by popular builders.
     *
     * @return bool
     */
    private function is_builder_preview() {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check of query args to detect builder preview; no state is changed.
        if ( empty( $_GET ) || !is_array( $_GET ) ) {
            return false;
        }
        $keys = array(
            // Cornerstone / Themeco.
            'cornerstone',
            'cornerstone_preview',
            'cs_preview',
            'cs_iframe',
            'cs_refresh',
            // Elementor.
            'elementor-preview',
            'elementor_library',
            // Beaver Builder.
            'fl_builder',
            'fl_builder_preview',
            // Divi.
            'et_fb',
            'et_pb_preview',
            // Bricks.
            'bricks',
            'bricks_preview',
            // Oxygen.
            'ct_builder',
            'oxygen_iframe',
            // WPBakery.
            'vc_editable',
            'vcv-editable',
            // Breakdance.
            'breakdance',
            'breakdance_iframe',
            // Brizy.
            'brizy-edit',
            'brizy-edit-iframe',
            // Zion Builder.
            'zionbuilder-preview',
            // Thrive Architect.
            'tve',
        );
        foreach ( $keys as $key ) {
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check of query args to detect builder preview; no state is changed.
            if ( isset( $_GET[$key] ) ) {
                return true;
            }
        }
        // Elementor also signals via action=elementor in the query string.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check; no state change.
        if ( isset( $_GET['action'] ) && 'elementor' === $_GET['action'] ) {
            return true;
        }
        return false;
    }

    /**
     * Check if the current post content includes common WooCommerce shortcodes.
     *
     * @return bool
     */
    private function current_post_has_woocommerce_shortcodes() {
        if ( !is_singular() ) {
            return false;
        }
        global $post;
        if ( !$post instanceof \WP_Post ) {
            return false;
        }
        if ( empty( $post->post_content ) || !function_exists( 'has_shortcode' ) ) {
            return false;
        }
        $shortcodes = array(
            // Product rendering shortcodes that can include variation forms.
            'product_page',
            'product',
            'add_to_cart',
            'products',
            'product_category',
            'featured_products',
            'sale_products',
            'best_selling_products',
            'top_rated_products',
            'recent_products',
        );
        foreach ( $shortcodes as $shortcode ) {
            if ( has_shortcode( $post->post_content, $shortcode ) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if the current post content includes WooCommerce Gutenberg blocks.
     *
     * @return bool
     */
    private function current_post_has_woocommerce_blocks() {
        if ( !is_singular() || !function_exists( 'has_block' ) ) {
            return false;
        }
        global $post;
        if ( !$post instanceof \WP_Post ) {
            return false;
        }
        if ( empty( $post->post_content ) ) {
            return false;
        }
        $blocks = array(
            'woocommerce/all-products',
            'woocommerce/handpicked-products',
            'woocommerce/product-best-sellers',
            'woocommerce/product-category',
            'woocommerce/product-new',
            'woocommerce/product-on-sale',
            'woocommerce/product-top-rated',
            'woocommerce/products-by-attribute',
            'woocommerce/product-collection',
            'woocommerce/single-product',
            'woocommerce/product-template'
        );
        foreach ( $blocks as $block ) {
            if ( has_block( $block, $post ) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if any active sidebar contains WooCommerce product widgets.
     *
     * Widgets like "Products" or "Recently Viewed Products" can render variable
     * product cards on any page, so swatches assets must be available.
     *
     * @return bool
     */
    private function active_sidebars_have_product_widgets() {
        $sidebars = get_option( 'sidebars_widgets', array() );
        if ( !is_array( $sidebars ) ) {
            return false;
        }
        unset($sidebars['array_version']);
        $product_widget_prefixes = array(
            'woocommerce_products-',
            'woocommerce_recently_viewed_products-',
            'woocommerce_top_rated_products-',
            'woocommerce_product_categories-'
        );
        foreach ( $sidebars as $sidebar_id => $widgets ) {
            if ( 'wp_inactive_widgets' === $sidebar_id || !is_array( $widgets ) ) {
                continue;
            }
            if ( !is_active_sidebar( $sidebar_id ) ) {
                continue;
            }
            foreach ( $widgets as $widget_id ) {
                foreach ( $product_widget_prefixes as $prefix ) {
                    if ( 0 === strpos( (string) $widget_id, $prefix ) ) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Enqueue admin assets.
     *
     * @param string $hook Current admin page hook.
     */
    public function enqueue_admin_assets( $hook ) {
        // Only load the React admin app on our plugin settings page.
        // Other admin screens (product editor / term meta) enqueue their own lightweight assets.
        if ( !in_array( $hook, array('toplevel_page_b3-swatches', 'b3-swatches_page_b3-swatches-settings'), true ) ) {
            return;
        }
        wp_enqueue_media();
        $admin_handle = 'admin';
        $this->enqueuer->enqueue_asset( 'admin', 'src/admin/main.tsx', array('wp-element') );
        $style_handle = $this->resolve_inline_style_handle( $admin_handle, 'b3-wvs-inline-admin' );
        $this->enqueue_typography_inline_css(
            $style_handle,
            '#b3-wvs-admin-root ',
            true,
            'admin'
        );
    }

    /**
     * Resolve the CSS handle used for plugin inline styles.
     *
     * @param string $asset_handle    Asset handle key used by Enqueuer.
     * @param string $fallback_handle Fallback style handle.
     * @return string
     */
    private function resolve_inline_style_handle( $asset_handle, $fallback_handle ) {
        $prefix = 'b3-wvs-' . $asset_handle . '-';
        global $wp_styles;
        if ( isset( $wp_styles ) && $wp_styles instanceof \WP_Styles ) {
            $registered = ( is_array( $wp_styles->registered ) ? array_keys( $wp_styles->registered ) : array() );
            foreach ( $registered as $registered_handle ) {
                if ( 1 !== preg_match( '/^' . preg_quote( $prefix, '/' ) . '\\d+$/', $registered_handle ) ) {
                    continue;
                }
                if ( !wp_style_is( $registered_handle, 'enqueued' ) ) {
                    wp_enqueue_style( $registered_handle );
                }
                return $registered_handle;
            }
        }
        if ( !wp_style_is( $fallback_handle, 'registered' ) ) {
            wp_register_style(
                $fallback_handle,
                false,
                array(),
                ( defined( 'B3_WVS_VERSION' ) ? B3_WVS_VERSION : false )
            );
        }
        if ( !wp_style_is( $fallback_handle, 'enqueued' ) ) {
            wp_enqueue_style( $fallback_handle );
        }
        return $fallback_handle;
    }

    /**
     * Build and enqueue typography CSS via WordPress enqueue APIs.
     *
     * @param string $style_handle Target style handle.
     * @param string $prefix       CSS selector prefix.
     * @param bool   $is_admin_css Whether CSS is admin-scoped.
     * @param string $context      Context label for debug logging.
     */
    private function enqueue_typography_inline_css(
        $style_handle,
        $prefix,
        $is_admin_css,
        $context
    ) {
        try {
            $settings = \b3wvs_get_effective_settings();
            $families_meta = ( function_exists( 'b3wvs_get_typography_families_meta' ) ? b3wvs_get_typography_families_meta() : array() );
            $css = $this->typography_renderer->build_typography_styles(
                $settings,
                $prefix,
                $families_meta,
                $is_admin_css
            );
            if ( '' === trim( $css ) ) {
                return;
            }
            wp_add_inline_style( $style_handle, $css );
        } catch ( \Exception $e ) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging only when WP_DEBUG is enabled.
                error_log( 'B3 Swatches typography inline CSS error (' . $context . '): ' . $e->getMessage() );
            }
        }
    }

}
