<?php
/**
 * Sticky Controls for Elementor Advanced Tab
 *
 * Registers a "Sticky Controls" section under the Elementor Advanced tab
 * for all element types (sections, containers, widgets).
 *
 * @package Softech_Addons_for_Elementor
 */

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

/**
 * Class SITS_Sticky_Controls
 *
 * Hooks into Elementor to add sticky behaviour controls and
 * outputs the required data attributes on the frontend wrapper.
 */
class SITS_Sticky_Controls {

	/**
	 * Element types that receive the sticky controls.
	 *
	 * @var string[]
	 */
	private static $element_hooks = [
    'section'   => 'section_advanced',
    'column'    => 'section_advanced',
    'container' => 'section_layout',
    'common'    => '_section_style',   // covers ALL widgets
];

public static function init() {
    foreach ( self::$element_hooks as $type => $section_id ) {
        add_action(
            "elementor/element/{$type}/{$section_id}/after_section_end",
            [ __CLASS__, 'register_controls' ],
            10,
            2
        );
    }

	// Render data attributes on the frontend wrapper element.
		add_action( 'wp_head', [ __CLASS__, 'output_scroll_behavior_guard' ], 0 );
		add_action( 'elementor/frontend/element/before_render', [ __CLASS__, 'before_render' ] );
		add_action( 'elementor/frontend/section/before_render', [ __CLASS__, 'before_render' ] );
		add_action( 'elementor/frontend/container/before_render', [ __CLASS__, 'before_render' ] );
		add_action( 'elementor/frontend/widget/before_render', [ __CLASS__, 'before_render' ] );
	}

	/**
	 * Temporarily disable smooth scrolling during initial page load.
	 *
	 * When the browser restores scroll position on reload, any active
	 * `scroll-behavior: smooth` rule can animate the page from the top to the
	 * restored position. This guard forces `auto` before first paint and then
	 * restores the previous value after load.
	 */
	public static function output_scroll_behavior_guard() {
		if ( is_admin() || ! self::should_output_scroll_behavior_guard() ) {
			return;
		}
		?>
		<script id="sits-scroll-behavior-guard">
		(function () {
			var html = document.documentElement;
			var previousHtml = html && html.style ? html.style.scrollBehavior : "";
			var previousBody = "";

			function getBody() {
				return document.body;
			}

			function disableSmoothScroll() {
				var body = getBody();

				if (html && html.style && html.style.setProperty) {
					html.style.setProperty('scroll-behavior', 'auto', 'important');
				}
				if (body && body.style && body.style.setProperty) {
					previousBody = body.style.scrollBehavior || "";
					body.style.setProperty('scroll-behavior', 'auto', 'important');
				}
			}

			function restoreScrollBehavior() {
				var body = getBody();

				if (html && html.style) {
					if (previousHtml) {
						html.style.scrollBehavior = previousHtml;
					} else {
						html.style.removeProperty('scroll-behavior');
					}
				}
				if (body && body.style) {
					if (previousBody) {
						body.style.scrollBehavior = previousBody;
					} else {
						body.style.removeProperty('scroll-behavior');
					}
				}
			}

			disableSmoothScroll();
			window.addEventListener('load', function () {
				requestAnimationFrame(function () {
					restoreScrollBehavior();
				});
			}, { once: true });
		}());
		</script>
		<?php
	}

	/**
	 * Decide whether the scroll-behavior guard is needed on this request.
	 *
	 * The guard is only useful when the page contains at least one Elementor
	 * document that has sticky controls enabled, so we scan the current page and
	 * any active header/footer/theme-builder templates that can render here.
	 *
	 * @return bool
	 */
	private static function should_output_scroll_behavior_guard() {
		static $needs_guard = null;

		if ( null !== $needs_guard ) {
			return $needs_guard;
		}

		$candidate_ids = self::get_sticky_candidate_ids();

		foreach ( $candidate_ids as $post_id ) {
			if ( self::post_has_sticky_controls( $post_id ) ) {
				$needs_guard = true;
				return $needs_guard;
			}
		}

		$needs_guard = false;
		return $needs_guard;
	}

	/**
	 * Collect Elementor document IDs that can affect the current frontend request.
	 *
	 * @return int[]
	 */
	private static function get_sticky_candidate_ids() {
		$ids = [];

		$queried_id = absint( get_queried_object_id() );
		if ( $queried_id ) {
			$ids[] = $queried_id;
		}

		$post = get_post();
		if ( $post instanceof \WP_Post ) {
			$ids[] = (int) $post->ID;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only context detection for frontend output gating.
		if ( isset( $_GET['post'] ) ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only context detection for frontend output gating.
			$ids[] = absint( wp_unslash( $_GET['post'] ) );
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only context detection for frontend output gating.
		if ( isset( $_GET['elementor-preview'] ) ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only context detection for frontend output gating.
			$ids[] = absint( wp_unslash( $_GET['elementor-preview'] ) );
		}

		if ( class_exists( '\Softech_Addons_for_Elementor\Includes\SITS_Header_Footer_Builder' ) ) {
			$builder = \Softech_Addons_for_Elementor\Includes\SITS_Header_Footer_Builder::instance();

			if ( $builder ) {
				foreach ( [ 'header', 'footer', 'single_page', 'single_post', 'archive', '404_page' ] as $type ) {
					$template_id = $builder->get_best( $type );

					if ( $template_id ) {
						$ids[] = absint( $template_id );
					}
				}
			}
		}

		$ids = array_values( array_filter( array_unique( array_map( 'absint', $ids ) ) ) );

		return $ids;
	}

	/**
	 * Check whether a post's Elementor data contains any enabled sticky controls.
	 *
	 * @param int $post_id Post ID.
	 * @return bool
	 */
	private static function post_has_sticky_controls( $post_id ) {
		static $cache = [];

		$post_id = absint( $post_id );
		if ( ! $post_id ) {
			return false;
		}

		if ( array_key_exists( $post_id, $cache ) ) {
			return $cache[ $post_id ];
		}

		$raw_data = get_post_meta( $post_id, '_elementor_data', true );
		if ( empty( $raw_data ) ) {
			$cache[ $post_id ] = false;
			return false;
		}

		if ( is_string( $raw_data ) ) {
			$data = json_decode( $raw_data, true );
			if ( ! is_array( $data ) ) {
				$cache[ $post_id ] = false;
				return false;
			}
		} elseif ( is_array( $raw_data ) ) {
			$data = $raw_data;
		} else {
			$cache[ $post_id ] = false;
			return false;
		}

		$cache[ $post_id ] = self::elementor_data_has_sticky_controls( $data );
		return $cache[ $post_id ];
	}

	/**
	 * Recursively inspect Elementor data for the sticky enable flag.
	 *
	 * @param array $elements Elementor element tree.
	 * @return bool
	 */
	private static function elementor_data_has_sticky_controls( array $elements ) {
		foreach ( $elements as $element ) {
			if ( ! is_array( $element ) ) {
				continue;
			}

			$settings = isset( $element['settings'] ) && is_array( $element['settings'] ) ? $element['settings'] : [];
			if ( isset( $settings['sits_sticky_enable'] ) && 'yes' === $settings['sits_sticky_enable'] ) {
				return true;
			}

			if ( ! empty( $element['elements'] ) && is_array( $element['elements'] ) && self::elementor_data_has_sticky_controls( $element['elements'] ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Register all sticky controls on the given element.
	 *
	 * @param \Elementor\Element_Base $element  The current element.
	 * @param array                   $args     Section arguments.
	 */
	public static function register_controls( $element, $args ) {
		$element->start_controls_section(
			'sits_sticky_section',
			[
				'label' => esc_html__( 'Sticky Controls', 'softech-addons-for-elementor' ),
				'tab'   => \Elementor\Controls_Manager::TAB_ADVANCED,
			]
		);

		/* ── Enable Sticky ── */
		$element->add_control(
			'sits_sticky_enable',
			[
				'label'        => esc_html__( 'Enable Sticky', 'softech-addons-for-elementor' ),
				'type'         => \Elementor\Controls_Manager::SWITCHER,
				'label_on'     => esc_html__( 'Yes', 'softech-addons-for-elementor' ),
				'label_off'    => esc_html__( 'No', 'softech-addons-for-elementor' ),
				'return_value' => 'yes',
				'default'      => '',
			]
		);

		/* ── Sticky Direction ── */
		$element->add_control(
			'sits_sticky_direction',
			[
				'label'     => esc_html__( 'Sticky Direction', 'softech-addons-for-elementor' ),
				'type'      => \Elementor\Controls_Manager::SELECT,
				'default'   => 'top',
				'options'   => [
					'top'    => esc_html__( 'Top', 'softech-addons-for-elementor' ),
					'bottom' => esc_html__( 'Bottom', 'softech-addons-for-elementor' ),
				],
				'condition' => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		/* ── Sticky Offset ── */
		$element->add_responsive_control(
			'sits_sticky_offset',
			[
				'label'          => esc_html__( 'Sticky Offset (px)', 'softech-addons-for-elementor' ),
				'description'    => esc_html__( 'How many pixels the page should scroll before the sticky state activates.', 'softech-addons-for-elementor' ),
				'type'           => \Elementor\Controls_Manager::NUMBER,
				'default'        => 20,
				'tablet_default' => 20,
				'mobile_default' => 20,
				'min'            => 0,
				'max'            => 500,
				'step'           => 1,
				'condition'      => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		/* ── Sticky On Devices ── */
		$element->add_control(
			'sits_sticky_on',
			[
				'label'       => esc_html__( 'Sticky On Devices', 'softech-addons-for-elementor' ),
				'type'        => \Elementor\Controls_Manager::SELECT2,
				'multiple'    => true,
				'label_block' => true,
				'default'     => [ 'desktop', 'tablet', 'mobile' ],
				'options'     => [
					'desktop' => esc_html__( 'Desktop', 'softech-addons-for-elementor' ),
					'tablet'  => esc_html__( 'Tablet', 'softech-addons-for-elementor' ),
					'mobile'  => esc_html__( 'Mobile', 'softech-addons-for-elementor' ),
				],
				'condition'   => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		/* ── Z-Index ── */
		$element->add_control(
			'sits_sticky_z_index',
			[
				'label'     => esc_html__( 'Z-Index', 'softech-addons-for-elementor' ),
				'type'      => \Elementor\Controls_Manager::NUMBER,
				'default'   => 999,
				'min'       => 0,
				'max'       => 99999,
				'step'      => 1,
				'condition' => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		/* ── Sticky Animation ── */
		$element->add_control(
			'sits_sticky_animation',
			[
				'label'     => esc_html__( 'Sticky Animation', 'softech-addons-for-elementor' ),
				'type'      => \Elementor\Controls_Manager::SELECT,
				'default'   => 'none',
				'options'   => [
					'none'  => esc_html__( 'None', 'softech-addons-for-elementor' ),
					'fade'  => esc_html__( 'Fade', 'softech-addons-for-elementor' ),
					'slide' => esc_html__( 'Slide', 'softech-addons-for-elementor' ),
				],
				'condition' => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		/* ── Hide on Scroll Down ── */
		$element->add_control(
			'sits_sticky_hide_on_scroll',
			[
				'label'        => esc_html__( 'Hide on Scroll Down', 'softech-addons-for-elementor' ),
				'type'         => \Elementor\Controls_Manager::SWITCHER,
				'label_on'     => esc_html__( 'Yes', 'softech-addons-for-elementor' ),
				'label_off'    => esc_html__( 'No', 'softech-addons-for-elementor' ),
				'return_value' => 'yes',
				'default'      => '',
				'condition'    => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		/* ── Sticky Background (classic / gradient) ── */
		$element->add_group_control(
			\Elementor\Group_Control_Background::get_type(),
			[
				'name'      => 'sits_sticky_bg',
				'label'     => esc_html__( 'Sticky Background', 'softech-addons-for-elementor' ),
				'types'     => [ 'classic', 'gradient' ],
				'selector'  => '{{WRAPPER}}.sits-is-sticky',
				'fields_options' => [
					'color' => [
						'global' => [
							'default' => \Elementor\Core\Kits\Documents\Tabs\Global_Colors::COLOR_PRIMARY,
						],
					],
					'color_b' => [
						'global' => [
							'default' => \Elementor\Core\Kits\Documents\Tabs\Global_Colors::COLOR_PRIMARY,
						],
					],
				],
				'condition' => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		/* ── Sticky Box Shadow ── */
		$element->add_group_control(
			\Elementor\Group_Control_Box_Shadow::get_type(),
			[
				'name'      => 'sits_sticky_shadow',
				'label'     => esc_html__( 'Sticky Shadow', 'softech-addons-for-elementor' ),
				'selector'  => '{{WRAPPER}}.sits-is-sticky',
				'condition' => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		/* ── Custom CSS Classes ── */
		$element->add_control(
			'sits_sticky_css_classes',
			[
				'label'       => esc_html__( 'Custom CSS Classes', 'softech-addons-for-elementor' ),
				'type'        => \Elementor\Controls_Manager::TEXT,
				'default'     => '',
				'placeholder' => esc_html__( 'e.g. my-sticky-header', 'softech-addons-for-elementor' ),
				'description' => esc_html__( 'Space-separated CSS classes added while element is sticky.', 'softech-addons-for-elementor' ),
				'condition'   => [ 'sits_sticky_enable' => 'yes' ],
			]
		);

		$element->end_controls_section();
	}

	/**
	 * Output data attributes on the element wrapper so the JS can read them.
	 *
	 * @param \Elementor\Element_Base $element
	 */
	public static function before_render( $element ) {
		$settings = $element->get_settings_for_display();

		if ( empty( $settings['sits_sticky_enable'] ) || 'yes' !== $settings['sits_sticky_enable'] ) {
			return;
		}

		$devices       = ! empty( $settings['sits_sticky_on'] ) ? (array) $settings['sits_sticky_on'] : [ 'desktop', 'tablet', 'mobile' ];
		$z_index       = isset( $settings['sits_sticky_z_index'] ) ? absint( $settings['sits_sticky_z_index'] ) : 999;
		$offset        = isset( $settings['sits_sticky_offset'] ) ? absint( $settings['sits_sticky_offset'] ) : 20;
		$offset_tablet = isset( $settings['sits_sticky_offset_tablet'] ) && '' !== $settings['sits_sticky_offset_tablet'] ? absint( $settings['sits_sticky_offset_tablet'] ) : 20;
		$offset_mobile = isset( $settings['sits_sticky_offset_mobile'] ) && '' !== $settings['sits_sticky_offset_mobile'] ? absint( $settings['sits_sticky_offset_mobile'] ) : 20;
		$direction     = ! empty( $settings['sits_sticky_direction'] ) ? sanitize_key( $settings['sits_sticky_direction'] ) : 'top';
		$animation     = ! empty( $settings['sits_sticky_animation'] ) ? sanitize_key( $settings['sits_sticky_animation'] ) : 'none';
		$hide_down     = ! empty( $settings['sits_sticky_hide_on_scroll'] ) && 'yes' === $settings['sits_sticky_hide_on_scroll'] ? 'true' : 'false';
		$css_classes = ! empty( $settings['sits_sticky_css_classes'] ) ? self::sanitize_sticky_classes( $settings['sits_sticky_css_classes'] ) : '';

		$wrapper_attributes = [
			'data-sits-sticky'              => 'true',
			'data-sits-sticky-direction'    => $direction,
			'data-sits-sticky-offset'       => $offset,
			'data-sits-sticky-offset-tablet' => $offset_tablet,
			'data-sits-sticky-offset-mobile' => $offset_mobile,
			'data-sits-sticky-on'           => implode( ',', array_map( 'sanitize_key', $devices ) ),
			'data-sits-sticky-z-index'      => $z_index,
			'data-sits-sticky-animation'    => $animation,
			'data-sits-sticky-hide-down'    => $hide_down,
		];

		if ( '' !== $css_classes ) {
			$wrapper_attributes['data-sits-sticky-classes'] = $css_classes;
		}

		$element->add_render_attribute( '_wrapper', $wrapper_attributes );
	}

	/**
	 * Normalize space-separated CSS classes into valid class tokens.
	 *
	 * @param string $value Raw control value.
	 * @return string
	 */
	private static function sanitize_sticky_classes( $value ) {
		$tokens = preg_split( '/\s+/', trim( wp_strip_all_tags( (string) $value ) ) );

		if ( empty( $tokens ) ) {
			return '';
		}

		$classes = [];

		foreach ( $tokens as $token ) {
			$token = sanitize_html_class( $token );

			if ( '' !== $token ) {
				$classes[] = $token;
			}
		}

		return implode( ' ', array_values( array_unique( $classes ) ) );
	}
}
