<?php

namespace Calio\Widgets;

use Elementor\Controls_Manager;
use Elementor\Group_Control_Border;
use Elementor\Group_Control_Box_Shadow;
use Elementor\Group_Control_Typography;
use Elementor\Utils;

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

/**
 * Image scroll widget inspired by long-form showcase scrollers.
 */
class ImageFlowWidget extends BaseWidget
{
    public function get_name(): string
    {
        return 'calio-image-flow';
    }

    public function get_title(): string
    {
        return esc_html__('Image Flow', 'calio-addon');
    }

    public function get_icon(): string
    {
        return 'eicon-device-desktop';
    }

    public function get_style_depends(): array
    {
        return $this->resolve_widget_style_depends('calio-widget-image-flow');
    }

    public function get_script_depends(): array
    {
        return $this->resolve_widget_script_depends('calio-widget-image-flow');
    }

    public function get_keywords(): array
    {
        return array_merge(parent::get_keywords(), ['image', 'scroller', 'flow', 'scroll']);
    }

    protected function register_controls(): void
    {
        $this->register_content_controls();
        $this->register_link_controls();
        $this->register_wrapper_style_controls();
        $this->register_frame_style_controls();
        $this->register_image_style_controls();
        $this->register_caption_style_controls();
        $this->register_dragbar_style_controls();
    }

    private function register_content_controls(): void
    {
        $this->start_controls_section('section_image_flow_content', [
            'label' => esc_html__('Image', 'calio-addon'),
        ]);

        $this->add_control('image', [
            'label' => esc_html__('Choose Image', 'calio-addon'),
            'type' => Controls_Manager::MEDIA,
            'default' => [
                'url' => Utils::get_placeholder_image_src(),
            ],
            'dynamic' => [
                'active' => true,
            ],
        ]);

        $this->add_control('preset_pack', [
            'label' => esc_html__('Preset Pack', 'calio-addon'),
            'type' => Controls_Manager::SELECT,
            'default' => 'custom',
            'options' => [
                'custom' => esc_html__('Custom', 'calio-addon'),
                'showcase' => esc_html__('Showcase', 'calio-addon'),
                'device-mockup' => esc_html__('Device Mockup', 'calio-addon'),
                'portfolio-scroll' => esc_html__('Portfolio Scroll', 'calio-addon'),
            ],
        ]);

        $this->add_control('preset_notice', [
            'type' => Controls_Manager::RAW_HTML,
            'raw' => esc_html__('Preset packs apply recommended motion/frame/drag defaults. Switch to Custom for full manual control.', 'calio-addon'),
            'content_classes' => 'elementor-panel-alert elementor-panel-alert-info',
            'condition' => [
                'preset_pack!' => 'custom',
            ],
        ]);

        $this->add_control('scroll_direction', [
            'label' => esc_html__('Scroll Direction', 'calio-addon'),
            'type' => Controls_Manager::SELECT,
            'default' => 'bottom-top',
            'options' => [
                'bottom-top' => esc_html__('Bottom to Top', 'calio-addon'),
                'top-bottom' => esc_html__('Top to Bottom', 'calio-addon'),
                'right-left' => esc_html__('Right to Left', 'calio-addon'),
                'left-right' => esc_html__('Left to Right', 'calio-addon'),
            ],
        ]);

        $this->add_control('auto_scroll', [
            'label' => esc_html__('Auto Scroll', 'calio-addon'),
            'type' => Controls_Manager::SWITCHER,
            'return_value' => 'yes',
            'default' => 'yes',
        ]);

        $this->add_control('scroll_speed', [
            'label' => esc_html__('Scroll Speed (ms)', 'calio-addon'),
            'type' => Controls_Manager::NUMBER,
            'default' => 3800,
            'min' => 300,
            'max' => 30000,
            'step' => 100,
        ]);

        $this->add_control('loop_pause', [
            'label' => esc_html__('Pause Between Loops (ms)', 'calio-addon'),
            'type' => Controls_Manager::NUMBER,
            'default' => 600,
            'min' => 0,
            'max' => 10000,
            'step' => 50,
            'condition' => [
                'auto_scroll' => 'yes',
            ],
        ]);

        $this->add_control('pause_on_hover', [
            'label' => esc_html__('Pause On Hover', 'calio-addon'),
            'type' => Controls_Manager::SWITCHER,
            'return_value' => 'yes',
            'default' => 'yes',
            'condition' => [
                'auto_scroll' => 'yes',
            ],
        ]);

        $this->add_responsive_control('viewport_height', [
            'label' => esc_html__('Viewport Height', 'calio-addon'),
            'type' => Controls_Manager::SLIDER,
            'size_units' => ['px', 'vh'],
            'range' => [
                'px' => [
                    'min' => 120,
                    'max' => 1200,
                ],
                'vh' => [
                    'min' => 15,
                    'max' => 100,
                ],
            ],
            'default' => [
                'unit' => 'px',
                'size' => 360,
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-viewport' => 'height: {{SIZE}}{{UNIT}};',
            ],
        ]);

        $this->add_control('use_frame', [
            'label' => esc_html__('Device Frame', 'calio-addon'),
            'type' => Controls_Manager::SWITCHER,
            'return_value' => 'yes',
            'default' => '',
        ]);

        $this->add_control('frame_preset', [
            'label' => esc_html__('Frame Preset', 'calio-addon'),
            'type' => Controls_Manager::SELECT,
            'default' => 'desktop',
            'options' => [
                'desktop' => esc_html__('Desktop', 'calio-addon'),
                'browser-dark' => esc_html__('Browser Dark', 'calio-addon'),
                'tablet' => esc_html__('Tablet', 'calio-addon'),
                'phone' => esc_html__('Phone', 'calio-addon'),
                'custom' => esc_html__('Custom', 'calio-addon'),
            ],
            'condition' => [
                'use_frame' => 'yes',
            ],
        ]);

        $this->add_control('custom_frame_notch', [
            'label' => esc_html__('Show Notch', 'calio-addon'),
            'type' => Controls_Manager::SWITCHER,
            'return_value' => 'yes',
            'default' => '',
            'condition' => [
                'use_frame' => 'yes',
                'frame_preset' => 'custom',
            ],
        ]);

        $this->add_control('show_drag_bar', [
            'label' => esc_html__('Show Drag Bar', 'calio-addon'),
            'type' => Controls_Manager::SWITCHER,
            'return_value' => 'yes',
            'default' => '',
            'condition' => [
                'auto_scroll!' => 'yes',
            ],
        ]);

        $this->add_control('dragbar_position', [
            'label' => esc_html__('Drag Bar Position', 'calio-addon'),
            'type' => Controls_Manager::SELECT,
            'default' => 'auto',
            'options' => [
                'auto' => esc_html__('Auto', 'calio-addon'),
                'top' => esc_html__('Top', 'calio-addon'),
                'bottom' => esc_html__('Bottom', 'calio-addon'),
                'left' => esc_html__('Left', 'calio-addon'),
                'right' => esc_html__('Right', 'calio-addon'),
            ],
            'condition' => [
                'show_drag_bar' => 'yes',
                'auto_scroll!' => 'yes',
            ],
        ]);

        $this->add_control('dragbar_reverse', [
            'label' => esc_html__('Reverse Drag Direction', 'calio-addon'),
            'type' => Controls_Manager::SWITCHER,
            'return_value' => 'yes',
            'default' => '',
            'condition' => [
                'show_drag_bar' => 'yes',
                'auto_scroll!' => 'yes',
            ],
        ]);

        $this->add_control('dragbar_visibility', [
            'label' => esc_html__('Drag Bar Visibility', 'calio-addon'),
            'type' => Controls_Manager::SELECT,
            'default' => 'always',
            'options' => [
                'always' => esc_html__('Always', 'calio-addon'),
                'hover' => esc_html__('Show On Hover', 'calio-addon'),
            ],
            'condition' => [
                'show_drag_bar' => 'yes',
                'auto_scroll!' => 'yes',
            ],
        ]);

        $this->add_control('show_caption', [
            'label' => esc_html__('Show Caption', 'calio-addon'),
            'type' => Controls_Manager::SWITCHER,
            'return_value' => 'yes',
            'default' => '',
        ]);

        $this->add_control('caption', [
            'label' => esc_html__('Caption', 'calio-addon'),
            'type' => Controls_Manager::TEXTAREA,
            'rows' => 3,
            'default' => esc_html__('Showcase this long-form visual by hovering the image area.', 'calio-addon'),
            'dynamic' => [
                'active' => true,
            ],
            'condition' => [
                'show_caption' => 'yes',
            ],
        ]);

        $this->end_controls_section();
    }

    private function register_link_controls(): void
    {
        $this->start_controls_section('section_image_flow_link', [
            'label' => esc_html__('Link', 'calio-addon'),
        ]);

        $this->add_control('link_to', [
            'label' => esc_html__('Link To', 'calio-addon'),
            'type' => Controls_Manager::SELECT,
            'default' => 'lightbox',
            'options' => [
                'none' => esc_html__('None', 'calio-addon'),
                'lightbox' => esc_html__('Lightbox', 'calio-addon'),
                'external' => esc_html__('External URL', 'calio-addon'),
            ],
        ]);

        $this->add_control('external_link', [
            'label' => esc_html__('External URL', 'calio-addon'),
            'type' => Controls_Manager::URL,
            'placeholder' => esc_url('https://example.com'),
            'condition' => [
                'link_to' => 'external',
            ],
            'dynamic' => [
                'active' => true,
            ],
        ]);

        $this->add_control('show_link_hint', [
            'label' => esc_html__('Show Link Hint', 'calio-addon'),
            'type' => Controls_Manager::SWITCHER,
            'return_value' => 'yes',
            'default' => 'yes',
            'condition' => [
                'link_to!' => 'none',
            ],
        ]);

        $this->add_control('link_hint_text', [
            'label' => esc_html__('Hint Label', 'calio-addon'),
            'type' => Controls_Manager::TEXT,
            'default' => esc_html__('Open', 'calio-addon'),
            'condition' => [
                'link_to!' => 'none',
                'show_link_hint' => 'yes',
            ],
        ]);

        $this->end_controls_section();
    }

    private function register_wrapper_style_controls(): void
    {
        $this->start_controls_section('section_image_flow_style_wrapper', [
            'label' => esc_html__('Wrapper', 'calio-addon'),
            'tab' => Controls_Manager::TAB_STYLE,
        ]);

        $this->add_background_type_control('image_flow_wrapper_bg', '{{WRAPPER}} .calio-image-flow-shell');

        $this->add_group_control(Group_Control_Border::get_type(), [
            'name' => 'image_flow_wrapper_border',
            'selector' => '{{WRAPPER}} .calio-image-flow-shell',
        ]);

        $this->add_group_control(Group_Control_Box_Shadow::get_type(), [
            'name' => 'image_flow_wrapper_shadow',
            'selector' => '{{WRAPPER}} .calio-image-flow-shell',
        ]);

        $this->add_dimensions_control(
            'image_flow_wrapper_radius',
            esc_html__('Border Radius', 'calio-addon'),
            ['{{WRAPPER}} .calio-image-flow-shell', '{{WRAPPER}} .calio-image-flow-viewport'],
            'border-radius',
            ['size_units' => ['px', '%']]
        );

        $this->add_dimensions_control(
            'image_flow_wrapper_padding',
            esc_html__('Padding', 'calio-addon'),
            '{{WRAPPER}} .calio-image-flow-shell',
            'padding'
        );

        $this->end_controls_section();
    }

    private function register_image_style_controls(): void
    {
        $this->start_controls_section('section_image_flow_style_image', [
            'label' => esc_html__('Image', 'calio-addon'),
            'tab' => Controls_Manager::TAB_STYLE,
        ]);

        $this->add_control('image_fit', [
            'label' => esc_html__('Image Fit', 'calio-addon'),
            'type' => Controls_Manager::SELECT,
            'default' => 'cover',
            'options' => [
                'cover' => esc_html__('Cover', 'calio-addon'),
                'contain' => esc_html__('Contain', 'calio-addon'),
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-media' => 'object-fit: {{VALUE}};',
            ],
        ]);

        $this->add_control('image_transition_ease', [
            'label' => esc_html__('Transition Easing', 'calio-addon'),
            'type' => Controls_Manager::SELECT,
            'default' => 'linear',
            'options' => [
                'linear' => esc_html__('Linear', 'calio-addon'),
                'ease' => esc_html__('Ease', 'calio-addon'),
                'ease-in-out' => esc_html__('Ease In Out', 'calio-addon'),
                'smooth-out' => esc_html__('Smooth Out', 'calio-addon'),
                'smooth-in-out' => esc_html__('Smooth In Out', 'calio-addon'),
                'snappy' => esc_html__('Snappy', 'calio-addon'),
                'custom' => esc_html__('Custom Cubic Bezier', 'calio-addon'),
            ],
        ]);

        $this->add_control('image_transition_ease_custom', [
            'label' => esc_html__('Custom Easing', 'calio-addon'),
            'type' => Controls_Manager::TEXT,
            'default' => 'cubic-bezier(0.22, 1, 0.36, 1)',
            'placeholder' => 'cubic-bezier(0.22, 1, 0.36, 1)',
            'condition' => [
                'image_transition_ease' => 'custom',
            ],
        ]);

        $this->start_controls_tabs('image_flow_image_state_tabs');

        $this->start_controls_tab('image_flow_image_state_normal', [
            'label' => esc_html__('Normal', 'calio-addon'),
        ]);

        $this->add_control('image_opacity', [
            'label' => esc_html__('Opacity', 'calio-addon'),
            'type' => Controls_Manager::SLIDER,
            'size_units' => ['px'],
            'range' => [
                'px' => [
                    'min' => 0.1,
                    'max' => 1,
                    'step' => 0.01,
                ],
            ],
            'default' => [
                'size' => 1,
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-media' => 'opacity: {{SIZE}};',
            ],
        ]);

        $this->end_controls_tab();

        $this->start_controls_tab('image_flow_image_state_hover', [
            'label' => esc_html__('Hover', 'calio-addon'),
        ]);

        $this->add_control('image_opacity_hover', [
            'label' => esc_html__('Opacity', 'calio-addon'),
            'type' => Controls_Manager::SLIDER,
            'size_units' => ['px'],
            'range' => [
                'px' => [
                    'min' => 0.1,
                    'max' => 1,
                    'step' => 0.01,
                ],
            ],
            'default' => [
                'size' => 1,
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow:hover .calio-image-flow-media' => 'opacity: {{SIZE}};',
            ],
        ]);

        $this->end_controls_tab();
        $this->end_controls_tabs();

        $this->add_control('hint_text_color', [
            'label' => esc_html__('Hint Text Color', 'calio-addon'),
            'type' => Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-link-hint' => 'color: {{VALUE}};',
            ],
            'condition' => [
                'show_link_hint' => 'yes',
                'link_to!' => 'none',
            ],
        ]);

        $this->add_background_type_control(
            'image_flow_hint_bg',
            '{{WRAPPER}} .calio-image-flow-link-hint',
            [
                'condition' => [
                    'show_link_hint' => 'yes',
                    'link_to!' => 'none',
                ],
            ]
        );

        $this->end_controls_section();
    }

    private function register_frame_style_controls(): void
    {
        $this->start_controls_section('section_image_flow_style_frame', [
            'label' => esc_html__('Frame', 'calio-addon'),
            'tab' => Controls_Manager::TAB_STYLE,
            'condition' => [
                'use_frame' => 'yes',
            ],
        ]);

        $this->add_background_type_control('image_flow_frame_bg', '{{WRAPPER}} .calio-image-flow.has-frame .calio-image-flow-frame');

        $this->add_group_control(Group_Control_Border::get_type(), [
            'name' => 'image_flow_frame_border',
            'selector' => '{{WRAPPER}} .calio-image-flow.has-frame .calio-image-flow-frame',
        ]);

        $this->add_group_control(Group_Control_Box_Shadow::get_type(), [
            'name' => 'image_flow_frame_shadow',
            'selector' => '{{WRAPPER}} .calio-image-flow.has-frame .calio-image-flow-frame',
        ]);

        $this->add_dimensions_control(
            'image_flow_frame_padding',
            esc_html__('Frame Padding', 'calio-addon'),
            '{{WRAPPER}} .calio-image-flow.has-frame .calio-image-flow-frame',
            'padding'
        );

        $this->add_dimensions_control(
            'image_flow_frame_radius',
            esc_html__('Frame Radius', 'calio-addon'),
            '{{WRAPPER}} .calio-image-flow.has-frame .calio-image-flow-frame',
            'border-radius',
            ['size_units' => ['px', '%']]
        );

        $this->add_control('image_flow_notch_color', [
            'label' => esc_html__('Notch Color', 'calio-addon'),
            'type' => Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow.has-frame.has-frame-notch .calio-image-flow-frame::before' => 'background: {{VALUE}};',
            ],
            'condition' => [
                'frame_preset' => 'custom',
                'custom_frame_notch' => 'yes',
            ],
        ]);

        $this->end_controls_section();
    }

    private function register_caption_style_controls(): void
    {
        $this->start_controls_section('section_image_flow_style_caption', [
            'label' => esc_html__('Caption', 'calio-addon'),
            'tab' => Controls_Manager::TAB_STYLE,
            'condition' => [
                'show_caption' => 'yes',
            ],
        ]);

        $this->add_group_control(Group_Control_Typography::get_type(), [
            'name' => 'image_flow_caption_typography',
            'selector' => '{{WRAPPER}} .calio-image-flow-caption',
        ]);

        $this->add_text_paint_controls('image_flow_caption_text', esc_html__('Text Color', 'calio-addon'), '{{WRAPPER}} .calio-image-flow-caption');
        $this->add_background_type_control('image_flow_caption_bg', '{{WRAPPER}} .calio-image-flow-caption');
        $this->add_group_control(Group_Control_Border::get_type(), [
            'name' => 'image_flow_caption_border',
            'selector' => '{{WRAPPER}} .calio-image-flow-caption',
        ]);

        $this->add_dimensions_control(
            'image_flow_caption_radius',
            esc_html__('Border Radius', 'calio-addon'),
            '{{WRAPPER}} .calio-image-flow-caption',
            'border-radius',
            ['size_units' => ['px', '%']]
        );

        $this->add_dimensions_control(
            'image_flow_caption_padding',
            esc_html__('Padding', 'calio-addon'),
            '{{WRAPPER}} .calio-image-flow-caption',
            'padding'
        );

        $this->end_controls_section();
    }

    private function register_dragbar_style_controls(): void
    {
        $this->start_controls_section('section_image_flow_style_dragbar', [
            'label' => esc_html__('Drag Bar', 'calio-addon'),
            'tab' => Controls_Manager::TAB_STYLE,
            'condition' => [
                'show_drag_bar' => 'yes',
            ],
        ]);

        $this->add_control('dragbar_track_color', [
            'label' => esc_html__('Track Color', 'calio-addon'),
            'type' => Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-drag-range' => '--calio-flow-drag-track: {{VALUE}};',
            ],
        ]);

        $this->add_control('dragbar_progress_color', [
            'label' => esc_html__('Progress Color', 'calio-addon'),
            'type' => Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-drag-range' => '--calio-flow-drag-fill: {{VALUE}};',
            ],
        ]);

        $this->add_control('dragbar_thumb_color', [
            'label' => esc_html__('Thumb Color', 'calio-addon'),
            'type' => Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-drag-range' => '--calio-flow-drag-thumb: {{VALUE}};',
            ],
        ]);

        $this->add_control('dragbar_height', [
            'label' => esc_html__('Bar Height', 'calio-addon'),
            'type' => Controls_Manager::SLIDER,
            'size_units' => ['px'],
            'range' => [
                'px' => [
                    'min' => 2,
                    'max' => 20,
                ],
            ],
            'default' => [
                'unit' => 'px',
                'size' => 6,
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-drag-range' => '--calio-flow-drag-height: {{SIZE}}{{UNIT}};',
            ],
        ]);

        $this->add_control('dragbar_thumb_size', [
            'label' => esc_html__('Thumb Size', 'calio-addon'),
            'type' => Controls_Manager::SLIDER,
            'size_units' => ['px'],
            'range' => [
                'px' => [
                    'min' => 8,
                    'max' => 40,
                ],
            ],
            'default' => [
                'unit' => 'px',
                'size' => 14,
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-drag-range' => '--calio-flow-drag-thumb-size: {{SIZE}}{{UNIT}};',
            ],
        ]);

        $this->add_control('dragbar_thumb_border_width', [
            'label' => esc_html__('Thumb Border Width', 'calio-addon'),
            'type' => Controls_Manager::SLIDER,
            'size_units' => ['px'],
            'range' => [
                'px' => [
                    'min' => 0,
                    'max' => 10,
                ],
            ],
            'default' => [
                'unit' => 'px',
                'size' => 0,
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-drag-range' => '--calio-flow-drag-thumb-border-size: {{SIZE}}{{UNIT}};',
            ],
        ]);

        $this->add_control('dragbar_thumb_border_color', [
            'label' => esc_html__('Thumb Border Color', 'calio-addon'),
            'type' => Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow-drag-range' => '--calio-flow-drag-thumb-border-color: {{VALUE}};',
            ],
        ]);

        $this->add_control('dragbar_spacing', [
            'label' => esc_html__('Drag Offset', 'calio-addon'),
            'type' => Controls_Manager::SLIDER,
            'size_units' => ['px'],
            'range' => [
                'px' => [
                    'min' => 0,
                    'max' => 40,
                ],
            ],
            'default' => [
                'unit' => 'px',
                'size' => 12,
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow' => '--calio-flow-drag-offset: {{SIZE}}{{UNIT}};',
            ],
        ]);

        $this->add_control('dragbar_length', [
            'label' => esc_html__('Bar Length', 'calio-addon'),
            'type' => Controls_Manager::SLIDER,
            'size_units' => ['px'],
            'range' => [
                'px' => [
                    'min' => 80,
                    'max' => 520,
                ],
            ],
            'default' => [
                'unit' => 'px',
                'size' => 180,
            ],
            'selectors' => [
                '{{WRAPPER}} .calio-image-flow' => '--calio-flow-drag-length: {{SIZE}}{{UNIT}};',
            ],
        ]);

        $this->end_controls_section();
    }

    protected function render(): void
    {
        $settings = $this->apply_preset_overrides($this->get_settings_for_display());
        $image_url = $this->resolve_image_url($settings['image'] ?? []);
        if ($image_url === '') {
            return;
        }

        $preset_pack = $this->sanitize_preset((string) ($settings['preset_pack'] ?? 'custom'));
        $direction = $this->sanitize_direction((string) ($settings['scroll_direction'] ?? 'bottom-top'));
        $auto_scroll = ($settings['auto_scroll'] ?? 'yes') === 'yes';
        $pause_on_hover = ($settings['pause_on_hover'] ?? 'yes') === 'yes';
        $speed = $this->normalize_int($settings['scroll_speed'] ?? 3800, 3800, 300, 30000);
        $loop_pause = $this->normalize_int($settings['loop_pause'] ?? 600, 600, 0, 10000);
        $caption = trim((string) ($settings['caption'] ?? ''));
        $show_caption = ($settings['show_caption'] ?? '') === 'yes' && $caption !== '';
        $hint_text = trim((string) ($settings['link_hint_text'] ?? ''));
        $show_hint = ($settings['show_link_hint'] ?? 'yes') === 'yes' && $hint_text !== '';
        $use_frame = ($settings['use_frame'] ?? '') === 'yes';
        $frame_preset = $this->sanitize_frame((string) ($settings['frame_preset'] ?? 'desktop'));
        $use_custom_notch = $use_frame
            && $frame_preset === 'custom'
            && ($settings['custom_frame_notch'] ?? '') === 'yes';
        $show_drag_bar = !$auto_scroll && ($settings['show_drag_bar'] ?? '') === 'yes';
        $drag_position = $this->sanitize_drag_position((string) ($settings['dragbar_position'] ?? 'auto'));
        $drag_axis = $this->resolve_drag_axis($direction, $drag_position);
        $drag_reverse = $show_drag_bar && ($settings['dragbar_reverse'] ?? '') === 'yes';
        $drag_visibility = $this->sanitize_drag_visibility((string) ($settings['dragbar_visibility'] ?? 'always'));
        $flow_ease = $this->resolve_ease(
            (string) ($settings['image_transition_ease'] ?? 'linear'),
            (string) ($settings['image_transition_ease_custom'] ?? '')
        );

        $image_alt = $this->resolve_image_alt($settings['image'] ?? [], $image_url);
        $link = $this->resolve_link($settings, $image_url);

        $classes = [
            'calio-image-flow',
            'preset-' . sanitize_html_class($preset_pack),
            'dir-' . sanitize_html_class($direction),
            $auto_scroll ? 'mode-auto' : 'mode-hover',
        ];
        if ($use_frame) {
            $classes[] = 'has-frame';
            $classes[] = 'frame-' . sanitize_html_class($frame_preset);
        }
        if ($use_custom_notch) {
            $classes[] = 'has-frame-notch';
        }
        if ($show_drag_bar) {
            $classes[] = 'has-drag-bar';
            $classes[] = 'drag-axis-' . sanitize_html_class($drag_axis);
            $classes[] = 'drag-pos-' . sanitize_html_class($drag_position);
            $classes[] = 'drag-vis-' . sanitize_html_class($drag_visibility);
        }

        $wrapper_attrs = [
            'class="' . esc_attr(implode(' ', $classes)) . '"',
            'data-direction="' . esc_attr($direction) . '"',
            'data-auto-scroll="' . esc_attr($auto_scroll ? 'yes' : 'no') . '"',
            'data-drag-bar="' . esc_attr($show_drag_bar ? 'yes' : 'no') . '"',
            'data-drag-axis="' . esc_attr($drag_axis) . '"',
            'data-drag-reverse="' . esc_attr($drag_reverse ? 'yes' : 'no') . '"',
            'data-drag-visibility="' . esc_attr($drag_visibility) . '"',
            'data-scroll-speed="' . esc_attr((string) $speed) . '"',
            'data-loop-pause="' . esc_attr((string) $loop_pause) . '"',
            'data-pause-on-hover="' . esc_attr($pause_on_hover ? 'yes' : 'no') . '"',
            'style="' . esc_attr('--calio-flow-speed:' . $speed . 'ms;--calio-flow-ease:' . $flow_ease . ';') . '"',
        ];

        echo '<div ' . wp_kses_data(implode(' ', $wrapper_attrs)) . '>';
        echo '<div class="calio-image-flow-shell">';

        if ($link !== null) {
            $link_rel = [];
            if ($link['nofollow']) {
                $link_rel[] = 'nofollow';
            }
            if ($link['target_blank']) {
                $link_rel[] = 'noopener';
                $link_rel[] = 'noreferrer';
            }
            echo '<a class="calio-image-flow-link" href="' . esc_url((string) $link['href']) . '"';
            if ($link['target_blank']) {
                echo ' target="' . esc_attr('_blank') . '"';
            }
            if ($link_rel !== []) {
                echo ' rel="' . esc_attr(implode(' ', array_unique($link_rel))) . '"';
            }
            if ($link['lightbox']) {
                echo ' data-elementor-open-lightbox="' . esc_attr('yes') . '"';
            }
            echo '>';
        }

        echo '<div class="calio-image-flow-frame">';
        echo '<div class="calio-image-flow-viewport">';
        echo '<div class="calio-image-flow-track">';
        echo '<img class="calio-image-flow-media" src="' . esc_url($image_url) . '" alt="' . esc_attr($image_alt) . '" loading="lazy" />';
        echo '</div>';
        if ($link !== null && $show_hint) {
            echo '<span class="calio-image-flow-link-hint" aria-hidden="true">' . esc_html($hint_text) . '</span>';
        }
        echo '</div>';
        echo '</div>';

        if ($link !== null) {
            echo '</a>';
        }

        if ($show_drag_bar) {
            echo '<div class="calio-image-flow-drag axis-' . esc_attr($drag_axis) . ' pos-' . esc_attr($drag_position) . '">';
            echo '<input class="calio-image-flow-drag-range" type="range" min="0" max="100" step="1" value="0" aria-orientation="' . esc_attr($drag_axis) . '" aria-label="' . esc_attr__('Image flow progress', 'calio-addon') . '" />';
            echo '</div>';
        }

        if ($show_caption) {
            echo '<div class="calio-image-flow-caption">' . esc_html($caption) . '</div>';
        }

        echo '</div>';
        echo '</div>';
    }

    /**
     * @param array<string, mixed> $settings Widget settings.
     * @return array{href: string, target_blank: bool, nofollow: bool, lightbox: bool}|null
     */
    private function resolve_link(array $settings, string $image_url): ?array
    {
        $link_to = (string) ($settings['link_to'] ?? 'lightbox');
        if ($link_to === 'none') {
            return null;
        }

        if ($link_to === 'lightbox') {
            return [
                'href' => $image_url,
                'target_blank' => false,
                'nofollow' => false,
                'lightbox' => true,
            ];
        }

        $external = $settings['external_link'] ?? [];
        if (!is_array($external)) {
            return null;
        }

        $href = trim((string) ($external['url'] ?? ''));
        if ($href === '') {
            return null;
        }

        return [
            'href' => $href,
            'target_blank' => !empty($external['is_external']),
            'nofollow' => !empty($external['nofollow']),
            'lightbox' => false,
        ];
    }

    /**
     * @param array{href: string, target_blank: bool, nofollow: bool, lightbox: bool} $link
     */
    private function build_link_attribute_fragment(array $link): string
    {
        $attrs = [];
        if ($link['target_blank']) {
            $attrs[] = 'target="_blank"';
        }

        $rel = [];
        if ($link['nofollow']) {
            $rel[] = 'nofollow';
        }
        if ($link['target_blank']) {
            $rel[] = 'noopener';
            $rel[] = 'noreferrer';
        }
        if ($rel !== []) {
            $attrs[] = 'rel="' . esc_attr(implode(' ', array_unique($rel))) . '"';
        }

        if ($link['lightbox']) {
            $attrs[] = 'data-elementor-open-lightbox="yes"';
        }

        return $attrs === [] ? '' : ' ' . implode(' ', $attrs);
    }

    /**
     * @param array<string, mixed> $image Image control payload.
     */
    private function resolve_image_url(array $image): string
    {
        $url = trim((string) ($image['url'] ?? ''));
        $attachment_id = !empty($image['id']) ? absint($image['id']) : 0;
        if ($attachment_id > 0) {
            $full = wp_get_attachment_image_url($attachment_id, 'full');
            if (is_string($full) && $full !== '') {
                return $full;
            }
        }

        return $url;
    }

    /**
     * @param array<string, mixed> $image Image control payload.
     */
    private function resolve_image_alt(array $image, string $fallback_url): string
    {
        $attachment_id = !empty($image['id']) ? absint($image['id']) : 0;
        if ($attachment_id > 0) {
            $alt = trim((string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true));
            if ($alt !== '') {
                return $alt;
            }
        }

        return wp_basename($fallback_url);
    }

    private function sanitize_direction(string $direction): string
    {
        $allowed = ['bottom-top', 'top-bottom', 'right-left', 'left-right'];
        if (!in_array($direction, $allowed, true)) {
            return 'bottom-top';
        }

        return $direction;
    }

    private function sanitize_frame(string $frame): string
    {
        $allowed = ['desktop', 'browser-dark', 'tablet', 'phone', 'custom'];
        if (!in_array($frame, $allowed, true)) {
            return 'desktop';
        }

        return $frame;
    }

    private function sanitize_drag_position(string $position): string
    {
        $allowed = ['auto', 'top', 'bottom', 'left', 'right'];
        if (!in_array($position, $allowed, true)) {
            return 'auto';
        }

        return $position;
    }

    private function resolve_drag_axis(string $direction, string $drag_position): string
    {
        if (in_array($drag_position, ['left', 'right'], true)) {
            return 'vertical';
        }

        if (in_array($drag_position, ['top', 'bottom'], true)) {
            return 'horizontal';
        }

        return in_array($direction, ['bottom-top', 'top-bottom'], true) ? 'vertical' : 'horizontal';
    }

    private function sanitize_drag_visibility(string $visibility): string
    {
        $allowed = ['always', 'hover'];
        if (!in_array($visibility, $allowed, true)) {
            return 'always';
        }

        return $visibility;
    }

    /**
     * @param array<string, mixed> $settings
     * @return array<string, mixed>
     */
    private function apply_preset_overrides(array $settings): array
    {
        $preset = $this->sanitize_preset((string) ($settings['preset_pack'] ?? 'custom'));

        if ($preset === 'showcase') {
            $settings['auto_scroll'] = 'yes';
            $settings['scroll_direction'] = 'bottom-top';
            $settings['scroll_speed'] = 2400;
            $settings['loop_pause'] = 320;
            $settings['pause_on_hover'] = 'yes';
        } elseif ($preset === 'device-mockup') {
            $settings['auto_scroll'] = 'yes';
            $settings['scroll_direction'] = 'top-bottom';
            $settings['scroll_speed'] = 3200;
            $settings['loop_pause'] = 420;
            $settings['pause_on_hover'] = 'yes';
            $settings['use_frame'] = 'yes';
            $settings['frame_preset'] = 'phone';
        } elseif ($preset === 'portfolio-scroll') {
            $settings['auto_scroll'] = '';
            $settings['scroll_direction'] = 'right-left';
            $settings['show_drag_bar'] = 'yes';
            $settings['dragbar_position'] = 'auto';
            $settings['dragbar_visibility'] = 'hover';
            $settings['dragbar_reverse'] = '';
            $settings['use_frame'] = '';
        }

        return $settings;
    }

    private function sanitize_preset(string $preset): string
    {
        $allowed = ['custom', 'showcase', 'device-mockup', 'portfolio-scroll'];
        if (!in_array($preset, $allowed, true)) {
            return 'custom';
        }

        return $preset;
    }

    private function resolve_ease(string $preset, string $custom): string
    {
        $map = [
            'linear' => 'linear',
            'ease' => 'ease',
            'ease-in-out' => 'ease-in-out',
            'smooth-out' => 'cubic-bezier(0.22, 1, 0.36, 1)',
            'smooth-in-out' => 'cubic-bezier(0.45, 0, 0.55, 1)',
            'snappy' => 'cubic-bezier(0.2, 0.9, 0.2, 1)',
        ];

        if ($preset === 'custom') {
            return $this->sanitize_custom_ease($custom);
        }

        return $map[$preset] ?? 'linear';
    }

    private function sanitize_custom_ease(string $value): string
    {
        $value = trim($value);
        if ($value === '') {
            return 'linear';
        }

        if (in_array($value, ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end'], true)) {
            return $value;
        }

        if (preg_match('/^cubic-bezier\(\s*-?\d*\.?\d+\s*,\s*-?\d*\.?\d+\s*,\s*-?\d*\.?\d+\s*,\s*-?\d*\.?\d+\s*\)$/i', $value) === 1) {
            return $value;
        }

        if (preg_match('/^steps\(\s*\d+\s*(,\s*(start|end)\s*)?\)$/i', $value) === 1) {
            return $value;
        }

        return 'linear';
    }

    /**
     * @param mixed $value Raw input.
     */
    private function normalize_int($value, int $fallback, int $min, int $max): int
    {
        $number = is_numeric($value) ? (int) $value : $fallback;
        return max($min, min($max, $number));
    }
}
