<?php
/**
 * TOC Front-end assets functions.
 *
 * @package AnchorKit_TOC
 */

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

/* ---------- FRONT-END SCRIPTS & STYLES ---------- */
if ( ! function_exists( 'anchorkit_toc_enqueue_assets' ) ) {
	/**
	 * Enqueue front-end TOC assets.
	 *
	 * @return void
	 */
	function anchorkit_toc_enqueue_assets() {
		if ( ! anchorkit_get_option( 'anchorkit_toc_enabled', false ) ) {
			return;
		}
		if ( ! is_singular() ) {
			return;
		}
		$post_types = anchorkit_get_option( 'anchorkit_toc_post_types', array( 'post', 'page' ) );
		if ( ! in_array( get_post_type(), $post_types, true ) ) {
			return;
		}

		$auto_insert_enabled = (bool) anchorkit_get_option( 'anchorkit_toc_automatic_insertion', true );
		$manual_toc_present  = function_exists( 'anchorkit_post_contains_manual_toc' ) ? anchorkit_post_contains_manual_toc() : false;

		if ( ! $auto_insert_enabled && ! $manual_toc_present ) {
			return;
		}

		// CSS – enqueue frontend TOC styles.
		wp_enqueue_style( 'anchorkit-toc-css', plugins_url( 'css/anchorkit-frontend.css', ANCHORKIT_PLUGIN_FILE ), array(), ANCHORKIT_PLUGIN_VERSION );

		// JS – build a clean URL that always points to the plugin root /js directory.
		wp_enqueue_script( 'anchorkit-toc-js', plugins_url( 'js/table-of-contents.js', ANCHORKIT_PLUGIN_FILE ), array(), ANCHORKIT_PLUGIN_VERSION, true );

		// Pro feature defaults (free version values).
		$sticky_enabled     = false;
		$scroll_spy         = false;
		$sticky_position    = 'content';
		$sticky_offset      = 20;
		$entrance_animation = false;
		$animation_type     = 'fade';
		$scroll_easing      = 'ease-in-out';
		$scroll_duration    = 500;

		// This block is stripped by Freemius in the free version.
		if ( anchorkit_fs() && anchorkit_fs()->is__premium_only() ) {
			if ( anchorkit_fs()->can_use_premium_code() ) {
				$sticky_enabled     = (bool) anchorkit_get_option( 'anchorkit_toc_sticky_enabled', false );
				$scroll_spy         = true;
				$sticky_position    = anchorkit_get_option( 'anchorkit_toc_sticky_position', 'content' );
				$sticky_offset      = (int) anchorkit_get_option( 'anchorkit_toc_sticky_offset', 20 );
				$entrance_animation = (bool) anchorkit_get_option( 'anchorkit_toc_entrance_animation', false );
				$animation_type     = anchorkit_get_option( 'anchorkit_toc_animation_type', 'fade' );
				$scroll_easing      = anchorkit_get_option( 'anchorkit_toc_scroll_easing', 'ease-in-out' );
				$scroll_duration    = (int) anchorkit_get_option( 'anchorkit_toc_scroll_duration', 500 );
			}
		}

		$hierarchical_js = anchorkit_get_option( 'anchorkit_toc_hierarchical_view', 'not_set' );

		/**
		 * Filter the smooth scroll offset value.
		 *
		 * @since 1.0.0
		 * @param int $scroll_offset The scroll offset in pixels.
		 */
		$scroll_offset = apply_filters( 'anchorkit_smooth_scroll_offset', (int) anchorkit_get_option( 'anchorkit_toc_scroll_offset', 0 ) );

		wp_localize_script(
			'anchorkit-toc-js',
			'anchorkitTocSettings',
			array(
				'smooth_scroll'      => (bool) anchorkit_get_option( 'anchorkit_toc_smooth_scroll', true ),
				'scroll_offset'      => $scroll_offset,
				'collapsible'        => (bool) anchorkit_get_option( 'anchorkit_toc_collapsible', true ),
				'initial_state'      => anchorkit_get_option( 'anchorkit_toc_initial_state', 'expanded' ),
				'hierarchical'       => ( 'not_set' === $hierarchical_js ) ? true : (bool) $hierarchical_js,
				'active_color'       => anchorkit_get_option( 'anchorkit_toc_active_link_color', '#00A0D2' ),
				// Pro features.
				'scroll_spy'         => $scroll_spy,
				'sticky'             => $sticky_enabled,
				'sticky_position'    => $sticky_position,
				'sticky_offset'      => $sticky_offset,
				'entrance_animation' => $entrance_animation,
				'animation_type'     => $animation_type,
				'scroll_easing'      => $scroll_easing,
				'scroll_duration'    => $scroll_duration,
			)
		);

		// Inline styles (theme vars etc.) - excluding custom CSS.
		$toc_inline_css = anchorkit_get_toc_inline_css();
		if ( ! empty( $toc_inline_css ) ) {
			wp_add_inline_style( 'anchorkit-toc-css', $toc_inline_css );
		}
	}

	add_action( 'wp_enqueue_scripts', 'anchorkit_toc_enqueue_assets' );
}

/**
 * Boost CSS specificity by prepending high-specificity wrapper to all selectors.
 *
 * Uses :not(#id) selectors to achieve ID-level specificity without actually
 * requiring IDs in the HTML. Since #anchorkit_boost will never exist,
 * :not(#anchorkit_boost) always matches, adding (1,0,0) to specificity.
 *
 * Example transformation:
 * - Input:  .my-class { color: red; }
 * - Output: html:not(#anchorkit_boost) body:not(#anchorkit_boost) .my-class { color: red; }
 * - Specificity change: (0,1,0) -> (2,1,2) - beats any class-based selector
 *
 * This allows user custom CSS to override plugin defaults without requiring
 * the user to understand CSS specificity or add !important to every rule.
 *
 * @param string $css Raw CSS content.
 * @return string CSS with boosted specificity.
 */
function anchorkit_boost_css_specificity( $css ) {
	// Preserve comments and @rules.
	$parts   = array();
	$boosted = '';

	// Split by /* and */ to preserve comments.
	$css = preg_replace_callback(
		'!/\*.*?\*/!s',
		function ( $matches ) use ( &$parts ) {
			$key           = '___COMMENT_' . count( $parts ) . '___';
			$parts[ $key ] = $matches[0];
			return $key;
		},
		$css
	);

	// Handle @media, @keyframes, @supports etc - process their content recursively.
	$css = preg_replace_callback(
		'/@(media|supports|container)[^{]+\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/s',
		function ( $matches ) use ( &$parts ) {
			$key = '___ATRULE_' . count( $parts ) . '___';
			// Recursively boost specificity inside the @rule.
			$inner         = anchorkit_boost_css_specificity( $matches[2] );
			$parts[ $key ] = '@' . $matches[1] . ' ' . trim( substr( $matches[0], strlen( $matches[1] ) + 1, strpos( $matches[0], '{' ) - strlen( $matches[1] ) - 1 ) ) . '{' . $inner . '}';
			return $key;
		},
		$css
	);

	// Don't boost @keyframes - they don't use selectors.
	$css = preg_replace_callback(
		'/@keyframes[^{]+\{[^}]*(?:\{[^}]*\}[^}]*)*\}/s',
		function ( $matches ) use ( &$parts ) {
			$key           = '___KEYFRAME_' . count( $parts ) . '___';
			$parts[ $key ] = $matches[0];
			return $key;
		},
		$css
	);

	// Boost regular selectors.
	// Match pattern: selector { declarations }.
	$css = preg_replace_callback(
		'/([^{}]+)\{([^{}]*)\}/s',
		function ( $matches ) {
			$selector     = trim( $matches[1] );
			$declarations = $matches[2];

			// Skip empty selectors or preserved placeholders.
			if ( empty( $selector ) || false !== strpos( $selector, '___' ) ) {
				return $matches[0];
			}

			// Split multiple selectors (comma-separated).
			$selectors         = array_map( 'trim', explode( ',', $selector ) );
			$boosted_selectors = array();

			foreach ( $selectors as $sel ) {
				// Don't boost if already starts with html or body or :root.
				if ( preg_match( '/^\s*(html|body|:root)\b/i', $sel ) ) {
					$boosted_selectors[] = $sel;
				} else {
					// Prepend with ID-level specificity using :not(#id).
					// Specificity added: (2, 0, 2) - Beats any number of classes.
					$boosted_selectors[] = 'html:not(#anchorkit_boost) body:not(#anchorkit_boost) ' . $sel;
				}
			}

			return implode( ', ', $boosted_selectors ) . '{' . $declarations . '}';
		},
		$css
	);

	// Restore preserved parts.
	foreach ( $parts as $key => $value ) {
		$css = str_replace( $key, $value, $css );
	}

	return $css;
}

/**
 * Output custom CSS in a separate style block with maximum priority.
 *
 * Security: CSS is sanitized twice (on save in helpers.php and on output here)
 * following the defense-in-depth principle. Only administrators can save settings.
 *
 * Specificity: Selectors are automatically wrapped with :not(#id) to achieve
 * ID-level specificity (2,0,2) which overrides any class-based selectors.
 *
 * Hooked to wp_enqueue_scripts with priority 100 to ensure main CSS is already enqueued.
 *
 * @return void
 */
if ( ! function_exists( 'anchorkit_toc_output_custom_css' ) ) {
	function anchorkit_toc_output_custom_css() {
		// Only proceed if main stylesheet is enqueued.
		if ( ! wp_style_is( 'anchorkit-toc-css', 'enqueued' ) ) {
			return;
		}

		$custom_css = anchorkit_get_option( 'anchorkit_toc_custom_css', '' );
		if ( empty( $custom_css ) ) {
			return;
		}

		// Sanitize CSS (defense in depth).
		$safe_css = wp_strip_all_tags( $custom_css );
		if ( empty( $safe_css ) ) {
			return;
		}

		// Boost specificity using ID-level :not() selectors to override plugin defaults.
		$boosted_css = anchorkit_boost_css_specificity( $safe_css );

		// Add as inline style attached to main CSS handle.
		wp_add_inline_style( 'anchorkit-toc-css', "/* User Custom CSS - ID-level specificity boost applied */\n" . $boosted_css );
	}
}

// Hook custom CSS after main assets are enqueued (priority 100 ensures it runs after priority 10).
add_action( 'wp_enqueue_scripts', 'anchorkit_toc_output_custom_css', 100 );

/**
 * Enqueue theme detection script early (Safari fix).
 *
 * This script MUST run before CSS to prevent FOUC (Flash of Unstyled Content).
 * Uses wp_register_script with false src to create a header script handle,
 * then wp_add_inline_script to add the detection logic.
 *
 * Priority 1 ensures it registers before other scripts.
 *
 * @return void
 */
if ( ! function_exists( 'anchorkit_toc_theme_detection_script' ) ) {
	function anchorkit_toc_theme_detection_script() {
		$theme = anchorkit_get_option( 'anchorkit_toc_theme', 'system' );

		// Only needed for system theme (auto light/dark).
		if ( 'system' !== $theme ) {
			return;
		}

		// Register a header script handle (no external file, in_footer=false for head placement).
		wp_register_script( 'anchorkit-theme-detect', false, array(), ANCHORKIT_PLUGIN_VERSION, false );
		wp_enqueue_script( 'anchorkit-theme-detect' );

		// Add the inline detection script.
		$inline_script = "(function () {
            if (!window.matchMedia) return;
            var darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
            var isDark = darkQuery.matches;
            var html = document.documentElement;
            if (isDark) {
                html.classList.add('anchorkit-prefers-dark');
                html.classList.remove('anchorkit-prefers-light');
            } else {
                html.classList.add('anchorkit-prefers-light');
                html.classList.remove('anchorkit-prefers-dark');
            }
            if (darkQuery.addEventListener) {
                darkQuery.addEventListener('change', function (e) {
                    if (e.matches) {
                        html.classList.add('anchorkit-prefers-dark');
                        html.classList.remove('anchorkit-prefers-light');
                    } else {
                        html.classList.add('anchorkit-prefers-light');
                        html.classList.remove('anchorkit-prefers-dark');
                    }
                    document.querySelectorAll('.anchorkit-toc-container.anchorkit-toc-theme-system').forEach(function (el) {
                        el.style.display = 'none';
                        el.offsetHeight;
                        el.style.display = '';
                    });
                });
            }
            // Safari fix: Force repaint after page loads.
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', function () {
                    setTimeout(function () {
                        document.querySelectorAll('.anchorkit-toc-container.anchorkit-toc-theme-system').forEach(function (el) {
                            var d = el.style.display;
                            el.style.display = 'none';
                            el.offsetHeight;
                            el.style.display = d || '';
                        });
                    }, 0);
                });
            } else {
                setTimeout(function () {
                    document.querySelectorAll('.anchorkit-toc-container.anchorkit-toc-theme-system').forEach(function (el) {
                        var d = el.style.display;
                        el.style.display = 'none';
                        el.offsetHeight;
                        el.style.display = d || '';
                    });
                }, 0);
            }
        })();";

		wp_add_inline_script( 'anchorkit-theme-detect', $inline_script );
	}

	add_action( 'wp_enqueue_scripts', 'anchorkit_toc_theme_detection_script', 1 );
}
