<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Sifency\Classes\Sifency_Addons_Widget_Base;
use Elementor\Controls_Manager;
use Elementor\Group_Control_Image_Size;
use Elementor\Utils;
use Sifency\Traits\Sifency_Addons_Custom_CSS_Controls;
use Sifency\Traits\Sifency_Addons_Image_Style_Controls;
use Sifency\Traits\Sifency_Addons_Help_Controls;
use Sifency\Traits\Sifency_Addons_Animation_Controls;
use Sifency\Traits\Sifency_Addons_Animation_Manager;
use Elementor\Group_Control_Css_Filter;

class Sifency_Advanced_Parallax extends Sifency_Addons_Widget_Base {
	use Sifency_Addons_Custom_CSS_Controls;
	use Sifency_Addons_Image_Style_Controls;
	use Sifency_Addons_Animation_Controls;
	use Sifency_Addons_Help_Controls;

	public function __construct( $data = array(), $args = null ) {
		parent::__construct( $data, $args );
		$this->wg_config = array(
			'name'           => 'sifency-advanced-parallax',
			'title'          => 'Sifency Advanced Parallax',
			'icon'           => 'eicon-image-rollover',
			'category'       => array( 'sifency-creative-widgets' ),
			'script_depends' => array( 'three-js', 'hover-effect', 'lenis', 'gsap', 'gsap-scrolltrigger', 'html2canvas', 'sifency-parallax-script' ),
			'style_depends'  => array( 'sifency-creative-style' ),
			'keywords'       => array( 'parallax', 'image', 'video', 'scroll effect', 'gsap', 'shutter', 'pixelate', 'disintegrate' ),
		);
	}

	// =========================================================================
	// CONTROLS
	// =========================================================================

	protected function register_controls() {

		// ── Media Settings ────────────────────────────────────────────────────
		$this->start_controls_section(
			'section_content',
			array( 'label' => esc_html__( 'Media Settings', 'sifency-addons' ) )
		);

		$this->add_control(
			'media_type',
			array(
				'label'   => esc_html__( 'Media Type', 'sifency-addons' ),
				'type'    => Controls_Manager::SELECT,
				'default' => 'image',
				'options' => array(
					'image' => esc_html__( 'Image', 'sifency-addons' ),
					'video' => esc_html__( 'Video (HTML5)', 'sifency-addons' ),
				),
			)
		);

		// ── Single image controls ─────────────────────────────────────────────
		$this->add_control(
			'image_source',
			array(
				'label'     => esc_html__( 'Image Source', 'sifency-addons' ),
				'type'      => Controls_Manager::SELECT,
				'default'   => 'custom',
				'options'   => array(
					'custom'   => esc_html__( 'Custom Image', 'sifency-addons' ),
					'featured' => esc_html__( 'Featured Image', 'sifency-addons' ),
					'metabox'  => esc_html__( 'Post Metabox / Custom Field', 'sifency-addons' ),
				),
				'condition' => array(
					'media_type'              => 'image',
					'enable_multiple_images!' => 'yes',
				),
			)
		);

		$this->add_control(
			'image',
			array(
				'label'     => esc_html__( 'Choose Image', 'sifency-addons' ),
				'type'      => Controls_Manager::MEDIA,
				'default'   => array( 'url' => Utils::get_placeholder_image_src() ),
				'condition' => array(
					'media_type'              => 'image',
					'enable_multiple_images!' => 'yes',
					'image_source'            => 'custom',
				),
			)
		);

		$this->add_control(
			'metabox_id',
			array(
				'label'       => esc_html__( 'Metabox ID / Custom Field Key', 'sifency-addons' ),
				'type'        => Controls_Manager::TEXT,
				'label_block' => true,
				'description' => esc_html__( 'Enter the meta key / ID of the custom field that stores the image attachment ID or URL.', 'sifency-addons' ),
				'condition'   => array(
					'media_type'              => 'image',
					'enable_multiple_images!' => 'yes',
					'image_source'            => 'metabox',
				),
			)
		);

		$this->add_control(
			'fallback_image',
			array(
				'label'     => esc_html__( 'Fallback Image', 'sifency-addons' ),
				'type'      => Controls_Manager::MEDIA,
				'default'   => array( 'url' => Utils::get_placeholder_image_src() ),
				'condition' => array(
					'media_type'              => 'image',
					'enable_multiple_images!' => 'yes',
					'image_source!'           => 'custom',
				),
			)
		);

		// ── Multiple image controls ───────────────────────────────────────────
		$this->add_control(
			'enable_multiple_images',
			array(
				'label'     => esc_html__( 'Multiple Images (Loop)', 'sifency-addons' ),
				'type'      => Controls_Manager::SWITCHER,
				'default'   => '',
				'label_on'  => esc_html__( 'Yes', 'sifency-addons' ),
				'label_off' => esc_html__( 'No', 'sifency-addons' ),
				'condition' => array( 'media_type' => 'image' ),
			)
		);

		$img_repeater = new \Elementor\Repeater();
		$img_repeater->add_control(
			'image_item',
			array(
				'label'   => esc_html__( 'Choose Image', 'sifency-addons' ),
				'type'    => Controls_Manager::MEDIA,
				'default' => array( 'url' => Utils::get_placeholder_image_src() ),
			)
		);
		$img_repeater->add_control(
			'image_duration',
			array(
				'label'   => esc_html__( 'Duration (s)', 'sifency-addons' ),
				'type'    => Controls_Manager::NUMBER,
				'default' => 3,
				'min'     => 1,
				'max'     => 20,
			)
		);

		$this->add_control(
			'image_list',
			array(
				'label'       => esc_html__( 'Image Sequence', 'sifency-addons' ),
				'type'        => Controls_Manager::REPEATER,
				'fields'      => $img_repeater->get_controls(),
				'condition'   => array(
					'media_type'             => 'image',
					'enable_multiple_images' => 'yes',
				),
				'title_field' => 'Image Sequence',
			)
		);

		$this->add_group_control(
			Group_Control_Image_Size::get_type(),
			array(
				'name'      => 'image',
				'default'   => 'full',
				'condition' => array( 'media_type' => 'image' ),
			)
		);

		// ── Single video controls ─────────────────────────────────────────────
		$this->add_control(
			'video',
			array(
				'label'       => esc_html__( 'Choose Video', 'sifency-addons' ),
				'type'        => Controls_Manager::MEDIA,
				'media_types' => array( 'video' ),
				'condition'   => array(
					'media_type'              => 'video',
					'enable_multiple_videos!' => 'yes',
				),
			)
		);

		// ── Multiple video controls ───────────────────────────────────────────
		$this->add_control(
			'enable_multiple_videos',
			array(
				'label'     => esc_html__( 'Multiple Videos (Loop)', 'sifency-addons' ),
				'type'      => Controls_Manager::SWITCHER,
				'default'   => '',
				'label_on'  => esc_html__( 'Yes', 'sifency-addons' ),
				'label_off' => esc_html__( 'No', 'sifency-addons' ),
				'condition' => array( 'media_type' => 'video' ),
			)
		);

		$video_repeater = new \Elementor\Repeater();
		$video_repeater->add_control(
			'video_item',
			array(
				'label'       => esc_html__( 'Choose Video', 'sifency-addons' ),
				'type'        => Controls_Manager::MEDIA,
				'media_types' => array( 'video' ),
			)
		);
		$video_repeater->add_control(
			'video_duration',
			array(
				'label'       => esc_html__( 'Duration (s)', 'sifency-addons' ),
				'type'        => Controls_Manager::NUMBER,
				'min'         => 1,
				'max'         => 100,
				'step'        => 0.5,
				'description' => esc_html__( 'Leave empty to play the full video.', 'sifency-addons' ),
			)
		);

		$this->add_control(
			'video_list',
			array(
				'label'       => esc_html__( 'Video Sequence', 'sifency-addons' ),
				'type'        => Controls_Manager::REPEATER,
				'fields'      => $video_repeater->get_controls(),
				'condition'   => array(
					'media_type'             => 'video',
					'enable_multiple_videos' => 'yes',
				),
				'title_field' => 'Video Sequence',
			)
		);

		// ── Transition controls ───────────────────────────────────────────────
		$this->add_control(
			'video_transition_effect',
			array(
				'label'      => esc_html__( 'Transition Effect', 'sifency-addons' ),
				'type'       => Controls_Manager::SELECT,
				'default'    => 'fade',
				'options'    => array(
					'fade'               => 'Fade',
					'fade-up'            => 'Fade Up',
					'crossfade'          => 'Crossfade',
					'clip-vertical'      => 'Clip Vertical',
					'clip-horizontal'    => 'Clip Horizontal',
					'wipe-down'          => 'Wipe Down',
					'wipe-up'            => 'Wipe Up',
					'circle'             => 'Circle Reveal',
					'zoom'               => 'Zoom Reveal',
					'swipe-left'         => 'Swipe Left',
					'swipe-right'        => 'Swipe Right',
					'swipe-up'           => 'Swipe Up',
					'swipe-down'         => 'Swipe Down',
					'flip'               => 'Flip Reveal',
					'liquid'             => 'Liquid Distortion',
					'clip-circle-top'    => 'Clip Circle Top',
					'clip-circle-bottom' => 'Clip Circle Bottom',
					'curve-wipe-left'    => 'Curve Wipe Left',
					'curve-wipe-right'   => 'Curve Wipe Right',
					'pill-expand'        => 'Pill Expand',
					'diagonal-reveal'    => 'Diagonal Reveal',
					'shutter-v'          => '🆕 Shutter (Vertical Blinds)',
					'shutter-h'          => '🆕 Shutter (Horizontal Blinds)',
					'pixelate'           => '🆕 Pixelated Dissolve',
					'disintegrate'       => '🆕 Disintegration Scatter',
				),
				'conditions' => array(
					'relation' => 'or',
					'terms'    => array(
						array(
							'name'     => 'enable_multiple_videos',
							'operator' => '==',
							'value'    => 'yes',
						),
						array(
							'name'     => 'enable_multiple_images',
							'operator' => '==',
							'value'    => 'yes',
						),
					),
				),
			)
		);

		$this->add_control(
			'pixel_grid_size',
			array(
				'label'       => esc_html__( 'Pixel Grid Size', 'sifency-addons' ),
				'type'        => Controls_Manager::SLIDER,
				'default'     => array( 'size' => 18 ),
				'range'       => array(
					'px' => array(
						'min'  => 6,
						'max'  => 40,
						'step' => 1,
					),
				),
				'description' => esc_html__( 'Number of pixel columns/rows. Higher = finer pixels.', 'sifency-addons' ),
				'condition'   => array( 'video_transition_effect' => 'pixelate' ),
			)
		);

		$this->add_control(
			'disintegrate_count',
			array(
				'label'       => esc_html__( 'Particle Count', 'sifency-addons' ),
				'type'        => Controls_Manager::SLIDER,
				'default'     => array( 'size' => 60 ),
				'range'       => array(
					'px' => array(
						'min'  => 20,
						'max'  => 120,
						'step' => 10,
					),
				),
				'description' => esc_html__( 'More particles = richer but heavier effect.', 'sifency-addons' ),
				'condition'   => array( 'video_transition_effect' => 'disintegrate' ),
			)
		);

		$this->add_control(
			'video_transition_scrub',
			array(
				'label'        => esc_html__( 'Scroll Scrub Transition', 'sifency-addons' ),
				'type'         => Controls_Manager::SWITCHER,
				'default'      => '',
				'label_on'     => esc_html__( 'Yes', 'sifency-addons' ),
				'label_off'    => esc_html__( 'No', 'sifency-addons' ),
				'return_value' => 'yes',
				'conditions'   => array(
					'relation' => 'or',
					'terms'    => array(
						array(
							'name'     => 'enable_multiple_videos',
							'operator' => '==',
							'value'    => 'yes',
						),
						array(
							'name'     => 'enable_multiple_images',
							'operator' => '==',
							'value'    => 'yes',
						),
					),
				),
			)
		);

		$this->add_control(
			'video_transition_pin',
			array(
				'label'        => esc_html__( 'Pin Container during Scrub', 'sifency-addons' ),
				'type'         => Controls_Manager::SWITCHER,
				'default'      => '',
				'label_on'     => esc_html__( 'Yes', 'sifency-addons' ),
				'label_off'    => esc_html__( 'No', 'sifency-addons' ),
				'return_value' => 'yes',
				'condition'    => array( 'video_transition_scrub' => 'yes' ),
			)
		);

		$this->add_control(
			'video_transition_scrub_height',
			array(
				'label'     => esc_html__( 'Scrub Scroll Distance (px)', 'sifency-addons' ),
				'type'      => Controls_Manager::SLIDER,
				'default'   => array( 'size' => 1000 ),
				'range'     => array(
					'px' => array(
						'min'  => 300,
						'max'  => 5000,
						'step' => 50,
					),
				),
				'condition' => array( 'video_transition_scrub' => 'yes' ),
			)
		);

		$this->add_control(
			'video_transition_duration',
			array(
				'label'      => esc_html__( 'Transition Duration (s)', 'sifency-addons' ),
				'type'       => Controls_Manager::SLIDER,
				'default'    => array( 'size' => 1 ),
				'range'      => array(
					's' => array(
						'min'  => 0.1,
						'max'  => 3,
						'step' => 0.1,
					),
				),
				'conditions' => array(
					'relation' => 'or',
					'terms'    => array(
						array(
							'name'     => 'enable_multiple_videos',
							'operator' => '==',
							'value'    => 'yes',
						),
						array(
							'name'     => 'enable_multiple_images',
							'operator' => '==',
							'value'    => 'yes',
						),
					),
				),
			)
		);

		$this->add_control(
			'liquid_displacement',
			array(
				'label'     => esc_html__( 'Displacement Image', 'sifency-addons' ),
				'type'      => Controls_Manager::MEDIA,
				'default'   => array( 'url' => Utils::get_placeholder_image_src() ),
				'condition' => array( 'video_transition_effect' => 'liquid' ),
			)
		);

		$this->add_control(
			'liquid_intensity',
			array(
				'label'     => esc_html__( 'Liquid Intensity', 'sifency-addons' ),
				'type'      => Controls_Manager::SLIDER,
				'default'   => array( 'size' => 0.2 ),
				'range'     => array(
					'px' => array(
						'min'  => 0,
						'max'  => 1,
						'step' => 0.01,
					),
				),
				'condition' => array( 'video_transition_effect' => 'liquid' ),
			)
		);

		$this->end_controls_section();

		// ── Parallax & Animation ──────────────────────────────────────────────
		$this->start_controls_section(
			'section_parallax_settings',
			array( 'label' => esc_html__( 'Parallax & Animation', 'sifency-addons' ) )
		);

		$this->add_control(
			'parallax_speed',
			array(
				'label'       => esc_html__( 'Parallax Intensity', 'sifency-addons' ),
				'type'        => Controls_Manager::SLIDER,
				'range'       => array(
					'%' => array(
						'min'  => 0,
						'max'  => 50,
						'step' => 5,
					),
				),
				'default'     => array(
					'unit' => '%',
					'size' => 20,
				),
				'description' => 'How much the image moves vertically. 20% is optimal.',
			)
		);

		$this->add_control(
			'enable_scale',
			array(
				'label'        => esc_html__( 'Zoom Out Effect', 'sifency-addons' ),
				'type'         => Controls_Manager::SWITCHER,
				'default'      => 'yes',
				'return_value' => 'yes',
				'description'  => 'Image will subtly zoom out as you scroll down.',
			)
		);

		$this->add_control(
			'enable_reveal',
			array(
				'label'       => esc_html__( 'Clip-Path Reveal', 'sifency-addons' ),
				'type'        => Controls_Manager::SWITCHER,
				'default'     => 'yes',
				'description' => 'Adds a curtain-reveal effect when the media enters the screen.',
			)
		);

		$this->add_control(
			'reveal_effect',
			array(
				'label'     => esc_html__( 'Reveal Animation', 'sifency-addons' ),
				'type'      => Controls_Manager::SELECT,
				'options'   => array(
					'clip'        => esc_html__( 'Clip Reveal', 'sifency-addons' ),
					'fade'        => esc_html__( 'Fade In', 'sifency-addons' ),
					'slide-up'    => esc_html__( 'Slide Up', 'sifency-addons' ),
					'slide-left'  => esc_html__( 'Slide Left', 'sifency-addons' ),
					'slide-right' => esc_html__( 'Slide Right', 'sifency-addons' ),
					'zoom'        => esc_html__( 'Zoom In', 'sifency-addons' ),
				),
				'default'   => 'clip',
				'condition' => array( 'enable_reveal' => 'yes' ),
			)
		);

		$this->add_control(
			'reveal_duration',
			array(
				'label'     => esc_html__( 'Reveal Duration', 'sifency-addons' ),
				'type'      => Controls_Manager::SLIDER,
				'default'   => array( 'size' => 1.25 ),
				'range'     => array(
					's' => array(
						'min'  => 0.3,
						'max'  => 3,
						'step' => 0.1,
					),
				),
				'condition' => array( 'enable_reveal' => 'yes' ),
			)
		);

		$this->add_control(
			'enable_scroll_smoother',
			array(
				'label'        => esc_html__( '🆕 ScrollSmoother Depth', 'sifency-addons' ),
				'type'         => Controls_Manager::SWITCHER,
				'default'      => '',
				'label_on'     => esc_html__( 'Yes', 'sifency-addons' ),
				'label_off'    => esc_html__( 'No', 'sifency-addons' ),
				'return_value' => 'yes',
				'description'  => esc_html__( 'Applies randomised speed depth via GSAP ScrollSmoother. Requires ScrollSmoother to be initialised globally on the page.', 'sifency-addons' ),
				'separator'    => 'before',
			)
		);

		$this->add_control(
			'smoother_speed_min',
			array(
				'label'     => esc_html__( 'Speed Min', 'sifency-addons' ),
				'type'      => Controls_Manager::SLIDER,
				'default'   => array( 'size' => 0.55 ),
				'range'     => array(
					'px' => array(
						'min'  => 0.1,
						'max'  => 1.5,
						'step' => 0.05,
					),
				),
				'condition' => array( 'enable_scroll_smoother' => 'yes' ),
			)
		);

		$this->add_control(
			'smoother_speed_max',
			array(
				'label'     => esc_html__( 'Speed Max', 'sifency-addons' ),
				'type'      => Controls_Manager::SLIDER,
				'default'   => array( 'size' => 0.85 ),
				'range'     => array(
					'px' => array(
						'min'  => 0.1,
						'max'  => 2.0,
						'step' => 0.05,
					),
				),
				'condition' => array( 'enable_scroll_smoother' => 'yes' ),
			)
		);

		$this->end_controls_section();

		$this->add_sifency_addons_help_controls();

		// ── Container Style ───────────────────────────────────────────────────
		$this->start_controls_section(
			'section_style',
			array(
				'label' => esc_html__( 'Container Style', 'sifency-addons' ),
				'tab'   => Controls_Manager::TAB_STYLE,
			)
		);

		$this->add_responsive_control(
			'container_height',
			array(
				'label'      => esc_html__( 'Container Height', 'sifency-addons' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px', 'vh' ),
				'range'      => array(
					'px' => array(
						'min' => 200,
						'max' => 1000,
					),
					'vh' => array(
						'min' => 10,
						'max' => 100,
					),
				),
				'selectors'  => array(
					'{{WRAPPER}} .sifency-parallax-wrapper' => 'height: {{SIZE}}{{UNIT}};',
				),
			)
		);

		$this->add_responsive_control(
			'container_min_height',
			array(
				'label'      => esc_html__( 'Container Min Height', 'sifency-addons' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px', 'vh' ),
				'range'      => array(
					'px' => array(
						'min' => 200,
						'max' => 1000,
					),
					'vh' => array(
						'min' => 10,
						'max' => 100,
					),
				),
				'selectors'  => array(
					'{{WRAPPER}} .sifency-parallax-wrapper' => 'min-height: {{SIZE}}{{UNIT}};',
				),
			)
		);

		$this->add_group_control(
			\Elementor\Group_Control_Background::get_type(),
			array(
				'name'     => 'overlay_background',
				'label'    => esc_html__( 'Overlay Background', 'sifency-addons' ),
				'types'    => array( 'classic', 'gradient' ),
				'selector' => '{{WRAPPER}} .sifency-parallax-overlay',
			)
		);

		$this->add_control(
			'overlay_blur',
			array(
				'label'      => esc_html__( 'Overlay Blur', 'sifency-addons' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px' ),
				'range'      => array(
					'px' => array(
						'min' => 0,
						'max' => 50,
					),
				),
				'selectors'  => array(
					'{{WRAPPER}} .sifency-parallax-overlay' => 'backdrop-filter: blur({{SIZE}}{{UNIT}}); -webkit-backdrop-filter: blur({{SIZE}}{{UNIT}});',
				),
			)
		);

		$this->add_control(
			'border_radius',
			array(
				'label'      => esc_html__( 'Border Radius', 'sifency-addons' ),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => array( 'px', '%', 'em' ),
				'selectors'  => array(
					'{{WRAPPER}} .sifency-parallax-wrapper' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				),
			)
		);

		$this->end_controls_section();

		// ── Video Style ───────────────────────────────────────────────────────
		$this->start_controls_section(
			'section_video_style',
			array(
				'label'     => esc_html__( 'Video Style', 'sifency-addons' ),
				'tab'       => Controls_Manager::TAB_STYLE,
				'condition' => array( 'media_type' => 'video' ),
			)
		);

		$this->add_control(
			'video_blend_mode',
			array(
				'label'     => esc_html__( 'Blend Mode', 'sifency-addons' ),
				'type'      => Controls_Manager::SELECT,
				'options'   => array(
					''            => esc_html__( 'Normal', 'sifency-addons' ),
					'multiply'    => 'Multiply',
					'screen'      => 'Screen',
					'overlay'     => 'Overlay',
					'darken'      => 'Darken',
					'lighten'     => 'Lighten',
					'color-dodge' => 'Color Dodge',
					'color-burn'  => 'Color Burn',
					'hard-light'  => 'Hard Light',
					'soft-light'  => 'Soft Light',
					'difference'  => 'Difference',
					'exclusion'   => 'Exclusion',
					'hue'         => 'Hue',
					'saturation'  => 'Saturation',
					'color'       => 'Color',
					'luminosity'  => 'Luminosity',
				),
				'selectors' => array(
					'{{WRAPPER}} video.sifency-parallax-media' => 'mix-blend-mode: {{VALUE}};',
				),
			)
		);

		$this->add_group_control(
			Group_Control_Css_Filter::get_type(),
			array(
				'name'     => 'video_css_filters',
				'selector' => '{{WRAPPER}} video.sifency-parallax-media',
			)
		);

		$this->end_controls_section();

		$this->add_sifency_addons_image_style_controls(
			array(
				'selector'  => '.sifency-parallax-wrapper img.sifency-parallax-media',
				'selectors' => array(
					'container'  => '.sifency-parallax-wrapper img.sifency-parallax-media',
					'image'      => '.sifency-parallax-wrapper img.sifency-parallax-media',
					'dimensions' => '.sifency-parallax-wrapper img.sifency-parallax-media',
				),
				'exclude'   => array( 'radius', 'tabs', 'transition' ),
				'condition' => array( 'media_type' => 'image' ),
			)
		);

		$this->add_Sifency_Addons_Animation_Controls();
		$this->add_Sifency_Addons_Custom_CSS_Controls();
	}

	// =========================================================================
	// PRIVATE HELPERS
	// =========================================================================

	/**
	 * Resolves a single Elementor media-field array to a usable URL.
	 *
	 * Resolution priority:
	 *   1. Group_Control_Image_Size (respects the size selector in settings)
	 *   2. wp_get_attachment_url()  (always current-site URL — survives import)
	 *   3. Stored URL              (last resort; may be stale after import)
	 *
	 * @param  array  $media_field       e.g. $settings['image'] = ['id'=>12,'url'=>'...']
	 * @param  string $size_control_name Name of the Group_Control_Image_Size ('image').
	 * @return string                    Resolved URL or empty string.
	 */
	private function resolve_media_url( array $media_field, string $size_control_name = 'image' ): string {
		$id  = ! empty( $media_field['id'] ) ? (int) $media_field['id'] : 0;
		$url = ! empty( $media_field['url'] ) ? $media_field['url'] : '';

		if ( $id > 0 ) {
			$settings = $this->get_settings_for_display();

			$sized = Group_Control_Image_Size::get_attachment_image_src( $id, $size_control_name, $settings );
			if ( ! empty( $sized ) ) {
				return $sized;
			}

			$raw = wp_get_attachment_url( $id );
			if ( ! empty( $raw ) ) {
				return $raw;
			}
		}

		return $url;
	}

	/**
	 * Resolves an entire repeater list into a portable array of media items.
	 *
	 * @param  array  $items          Repeater items from settings.
	 * @param  string $media_key      Field key inside each item ('image_item'|'video_item').
	 * @param  string $duration_key   Field key for duration ('image_duration'|'video_duration').
	 * @param  string $size_control   Size control name passed to resolve_media_url().
	 * @return array<int,array{url:string,duration:mixed,alt:string}>
	 */
	private function resolve_repeater_media( array $items, string $media_key, string $duration_key = '', string $size_control = 'image' ): array {
		$resolved = array();

		foreach ( $items as $item ) {
			if ( empty( $item[ $media_key ] ) ) {
				continue;
			}

			$url = $this->resolve_media_url( $item[ $media_key ], $size_control );

			if ( empty( $url ) ) {
				continue;
			}

			$resolved[] = array(
				'url'      => $url,
				'duration' => ( ! empty( $duration_key ) && isset( $item[ $duration_key ] ) ) ? $item[ $duration_key ] : '',
				'alt'      => $item[ $media_key ]['alt'] ?? '',
			);
		}

		return $resolved;
	}

	/**
	 * Appends scrub-related data attributes to the reference array when enabled.
	 *
	 * @param array $attrs    Reference to the data-attributes array.
	 * @param array $settings Widget settings.
	 */
	private function maybe_add_scrub_attrs( array &$attrs, array $settings ): void {
		$is_scrub = ! empty( $settings['video_transition_scrub'] )
			&& in_array( $settings['video_transition_scrub'], array( 'yes', 'true', '1', true ), true );

		if ( ! $is_scrub ) {
			return;
		}

		$attrs['data-scrub-transition'] = 'yes';

		if ( ! empty( $settings['video_transition_pin'] ) && 'yes' === $settings['video_transition_pin'] ) {
			$attrs['data-scrub-pin'] = 'yes';
		}

		$attrs['data-scrub-height'] = $settings['video_transition_scrub_height']['size'] ?? 1000;
	}

	/**
	 * Resolves the single-image URL covering custom / featured / metabox sources.
	 *
	 * @param  array $settings Widget settings.
	 * @return string
	 */
	private function resolve_single_image( array $settings ): string {
		$image_source = $settings['image_source'] ?? 'custom';
		$img_id       = 0;
		$img_url      = '';

		if ( 'featured' === $image_source ) {

			$img_id = (int) get_post_thumbnail_id();

		} elseif ( 'metabox' === $image_source && ! empty( $settings['metabox_id'] ) ) {

			$meta_key = sanitize_text_field( $settings['metabox_id'] );
			$meta_val = get_post_meta( get_the_ID(), $meta_key, true );

			if ( ! empty( $meta_val ) ) {
				if ( is_numeric( $meta_val ) ) {
					$img_id = (int) $meta_val;

				} elseif ( is_array( $meta_val ) ) {
					$img_id  = isset( $meta_val['id'] ) ? (int) $meta_val['id']
							: ( isset( $meta_val['ID'] ) ? (int) $meta_val['ID'] : 0 );
					$img_url = $meta_val['url'] ?? ( $meta_val['src'] ?? '' );

				} elseif ( is_string( $meta_val ) ) {
					if ( filter_var( $meta_val, FILTER_VALIDATE_URL ) ) {
						$img_url = $meta_val;
					} else {
						$decoded = json_decode( $meta_val, true );
						if ( is_array( $decoded ) ) {
							$img_id  = isset( $decoded['id'] ) ? (int) $decoded['id']
									: ( isset( $decoded['ID'] ) ? (int) $decoded['ID'] : 0 );
							$img_url = $decoded['url'] ?? ( $decoded['src'] ?? '' );
						} elseif ( is_numeric( trim( $meta_val ) ) ) {
							$img_id = (int) trim( $meta_val );
						}
					}
				}
			}
		} else {
			// 'custom'
			$img_id  = (int) ( $settings['image']['id'] ?? 0 );
			$img_url = $settings['image']['url'] ?? '';
		}

		// Resolve via central helper.
		$resolved = $this->resolve_media_url(
			array(
				'id'  => $img_id,
				'url' => $img_url,
			),
			'image'
		);

		// Fallback image when source is not 'custom'.
		if ( empty( $resolved ) && 'custom' !== $image_source ) {
			$resolved = $this->resolve_media_url( $settings['fallback_image'] ?? array(), 'image' );
		}

		return $resolved;
	}

	/**
	 * Builds the final wrapper HTML attribute string, merging Elementor classes,
	 * animation attributes and parallax data-attributes.
	 *
	 * @param  array  $parallax_data_attrs key→value map of data-* attributes.
	 * @param  object $animation_manager   Sifency_Addons_Animation_Manager instance.
	 * @param  string $animation_target    'wrapper' or other.
	 * @return string                      Pre-escaped attribute string ready for echo.
	 */
	private function build_wrapper_attrs( array $parallax_data_attrs, $animation_manager, string $animation_target ): string {
		$temp_key = 'sifency_temp_class_for_parsing';
		$this->add_render_attribute( 'wrapper', 'class', $temp_key );
		$raw_attrs = $this->get_render_attribute_string( 'wrapper' );
		$this->remove_render_attribute( 'wrapper', 'class', $temp_key );

		$elementor_classes = array();
		if ( preg_match( '/class="([^"]*)"/', $raw_attrs, $matches ) ) {
			$elementor_classes = array_filter(
				explode( ' ', $matches[1] ),
				fn( $c ) => $c !== $temp_key && $c !== ''
			);
		}
		$elementor_classes[] = 'sifency-parallax-container';

		if ( 'wrapper' === $animation_target ) {
			$merged = $animation_manager->get_wrapper_attributes( $elementor_classes );
			$parsed = array();
			if ( preg_match_all( '/(\S+)="([^"]*)"/', $merged, $m, PREG_SET_ORDER ) ) {
				foreach ( $m as $match ) {
					$parsed[ $match[1] ] = $match[2];
				}
			}
			foreach ( $parallax_data_attrs as $key => $value ) {
				$parsed[ $key ] = $value;
			}
			$parts = array();
			foreach ( $parsed as $key => $value ) {
				$parts[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
			}
			return implode( ' ', $parts );
		}

		// Animation target is NOT the wrapper — attach data attrs directly.
		$all_classes  = array_unique( array_filter( $elementor_classes ) );
		$class_string = ! empty( $all_classes )
			? ' class="' . esc_attr( implode( ' ', $all_classes ) ) . '"'
			: '';

		$data_str = '';
		foreach ( $parallax_data_attrs as $key => $value ) {
			$data_str .= sprintf( ' %s="%s"', esc_attr( $key ), esc_attr( $value ) );
		}

		return $class_string . $data_str;
	}

	// =========================================================================
	// RENDER
	// =========================================================================

	protected function render_dom( array $context ): void {
		$settings   = $this->get_settings_for_display();
		$wrapper_id = esc_attr( $context['dom_id'] );

		$media_type   = $settings['media_type'] ?? 'image';
		$multi_images = ( $settings['enable_multiple_images'] ?? '' ) === 'yes';
		$multi_videos = ( $settings['enable_multiple_videos'] ?? '' ) === 'yes';
		$transition   = $settings['video_transition_effect'] ?? 'fade';

		// ── 1. Resolve ALL media up-front ─────────────────────────────────────
		// Resolving here (from IDs) means URLs are always current-site-correct,
		// surviving XML / OCDI imports where stored URLs become stale.

		$resolved_image_list = array();
		if ( 'image' === $media_type && $multi_images && ! empty( $settings['image_list'] ) ) {
			$resolved_image_list = $this->resolve_repeater_media(
				$settings['image_list'],
				'image_item',
				'image_duration',
				'image'
			);
		}

		$resolved_video_list = array();
		if ( 'video' === $media_type && $multi_videos && ! empty( $settings['video_list'] ) ) {
			$resolved_video_list = $this->resolve_repeater_media(
				$settings['video_list'],
				'video_item',
				'video_duration',
				'image'  // size control irrelevant for video; ID→url via wp_get_attachment_url
			);
		}

		$single_image_url = '';
		if ( 'image' === $media_type && ! $multi_images ) {
			$single_image_url = $this->resolve_single_image( $settings );
		}

		$single_video_url = '';
		if ( 'video' === $media_type && ! $multi_videos ) {
			$single_video_url = $this->resolve_media_url( $settings['video'] ?? array(), 'image' );
		}

		// ── 2. Build data-attributes ──────────────────────────────────────────
		$parallax_data_attrs = array(
			'data-speed'           => $settings['parallax_speed']['size'] ?? 20,
			'data-scale'           => ( ( $settings['enable_scale'] ?? '' ) === 'yes' ) ? 'true' : 'false',
			'data-reveal'          => ( ( $settings['enable_reveal'] ?? '' ) === 'yes' ) ? 'true' : 'false',
			'data-reveal-effect'   => $settings['reveal_effect'] ?? 'clip',
			'data-reveal-duration' => $settings['reveal_duration']['size'] ?? 1.25,
		);

		// ScrollSmoother depth.
		if ( ( $settings['enable_scroll_smoother'] ?? '' ) === 'yes' ) {
			$parallax_data_attrs['data-scroll-smoother']    = 'true';
			$parallax_data_attrs['data-smoother-speed-min'] = $settings['smoother_speed_min']['size'] ?? 0.55;
			$parallax_data_attrs['data-smoother-speed-max'] = $settings['smoother_speed_max']['size'] ?? 0.85;
		}

		// Multiple video sequence.
		if ( ! empty( $resolved_video_list ) ) {
			$parallax_data_attrs['data-videos']              = wp_json_encode( $resolved_video_list );
			$parallax_data_attrs['data-transition']          = $transition;
			$parallax_data_attrs['data-transition-duration'] = $settings['video_transition_duration']['size'] ?? 1;
			$this->maybe_add_scrub_attrs( $parallax_data_attrs, $settings );
		}

		// Multiple image sequence.
		if ( ! empty( $resolved_image_list ) ) {
			$parallax_data_attrs['data-images']              = wp_json_encode( $resolved_image_list );
			$parallax_data_attrs['data-transition']          = $transition;
			$parallax_data_attrs['data-transition-duration'] = $settings['video_transition_duration']['size'] ?? 1;
			$this->maybe_add_scrub_attrs( $parallax_data_attrs, $settings );

			if ( 'liquid' === $transition ) {
				$parallax_data_attrs['data-disp']      = $this->resolve_media_url( $settings['liquid_displacement'] ?? array(), 'image' );
				$parallax_data_attrs['data-intensity'] = $settings['liquid_intensity']['size'] ?? 0.2;
			}

			if ( 'pixelate' === $transition ) {
				$parallax_data_attrs['data-pixel-grid'] = $settings['pixel_grid_size']['size'] ?? 18;
			}

			if ( 'disintegrate' === $transition ) {
				$parallax_data_attrs['data-disintegrate-count'] = $settings['disintegrate_count']['size'] ?? 60;
			}
		}

		// ── 3. Build wrapper attributes ───────────────────────────────────────
		$animation_manager   = new Sifency_Addons_Animation_Manager( $settings );
		$animation_target    = $settings['sifency_animation_target'] ?? 'wrapper';
		$final_wrapper_attrs = $this->build_wrapper_attrs( $parallax_data_attrs, $animation_manager, $animation_target );

		// ── 4. Render HTML ────────────────────────────────────────────────────
		$this->sifency_addons_render_custom_css();
		?>
		<div id="<?php echo esc_attr( $wrapper_id ); ?>" 
		<?php
			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- pre-escaped in build_wrapper_attrs()
			echo $final_wrapper_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		?>
		>

			<div class="sifency-parallax-wrapper">

				<?php
				// ── Branch A: Liquid canvas (multiple images, liquid transition) ─
				if ( 'image' === $media_type && $multi_images && 'liquid' === $transition && count( $resolved_image_list ) >= 2 ) :
					$disp_url  = $this->resolve_media_url( $settings['liquid_displacement'] ?? array(), 'image' );
					$intensity = $settings['liquid_intensity']['size'] ?? 0.2;
					?>
					<div class="sifency-fx-container sifency-parallax-media"
						data-anim-type="liquid"
						data-img1="<?php echo esc_url( $resolved_image_list[0]['url'] ); ?>"
						data-img2="<?php echo esc_url( $resolved_image_list[1]['url'] ); ?>"
						data-disp="<?php echo esc_url( $disp_url ); ?>"
						data-intensity="<?php echo esc_attr( $intensity ); ?>">
						<canvas></canvas>
					</div>

					<?php
					// ── Branch B: Multiple videos ─────────────────────────────────
				elseif ( 'video' === $media_type && $multi_videos && ! empty( $resolved_video_list ) ) :
					foreach ( $resolved_video_list as $index => $item ) :
						?>
					<video class="sifency-parallax-media sifency-video-item"
							data-index="<?php echo esc_attr( $index ); ?>"
							src="<?php echo esc_url( $item['url'] ); ?>"
							style="opacity:<?php echo ( 0 === $index ? '1' : '0' ); ?>; z-index:<?php echo absint( 10 + $index ); ?>;"
							autoplay muted loop playsinline></video>
						<?php
					endforeach;

					// ── Branch C: Single video ────────────────────────────────────
				elseif ( 'video' === $media_type && ! $multi_videos && ! empty( $single_video_url ) ) :
					?>
					<video class="sifency-parallax-media sifency-video-main"
							src="<?php echo esc_url( $single_video_url ); ?>"
							autoplay muted loop playsinline></video>

					<?php
					// ── Branch D: Multiple images (non-liquid) ────────────────────
				elseif ( 'image' === $media_type && $multi_images && ! empty( $resolved_image_list ) ) :
					foreach ( $resolved_image_list as $index => $item ) :
						?>
					<img class="sifency-parallax-media sifency-image-item"
						data-index="<?php echo esc_attr( $index ); ?>"
						src="<?php echo esc_url( $item['url'] ); ?>"
						style="opacity:<?php echo ( 0 === $index ? '1' : '0' ); ?>; z-index:<?php echo absint( 10 + $index ); ?>;"
						alt="<?php echo esc_attr( $item['alt'] ); ?>">
						<?php
					endforeach;

					// ── Branch E: Single image ────────────────────────────────────
				elseif ( 'image' === $media_type && ! $multi_images && ! empty( $single_image_url ) ) :
					?>
					<img class="sifency-parallax-media sifency-image-main"
						src="<?php echo esc_url( $single_image_url ); ?>"
						alt="<?php echo esc_attr( $settings['image']['alt'] ?? '' ); ?>">

				<?php endif; ?>

				<div class="sifency-parallax-overlay"></div>

			</div><!-- /.sifency-parallax-wrapper -->
		</div>
		<?php
	}
}
