<?php
/**
 * Widget Name: Gallery Listing
 * Description: A self-contained, filterable image gallery with five distinctive
 *              layouts — uniform grid, masonry, hover-overlay, polaroid cards and a
 *              featured metro mosaic. Supports category filter tabs, a built-in
 *              vanilla-JS lightbox with keyboard navigation, lazy-loaded images,
 *              several hover effects and full style controls. Only the selected style
 *              CSS file is loaded on the frontend.
 *
 * @package    captain-widgets-kit
 * @subpackage captain-widgets-kit/includes
 * @since      1.5.0
 */

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

use Elementor\Widget_Base;
use Elementor\Controls_Manager;
use Elementor\Repeater;
use Elementor\Utils;
use Elementor\Group_Control_Image_Size;
use Elementor\Group_Control_Typography;
use Elementor\Group_Control_Box_Shadow;
use Elementor\Group_Control_Border;

/**
 * Class Captwiki_Gallery_Listing
 *
 * @since 1.5.0
 */
class Captwiki_Gallery_Listing extends Widget_Base {

	/**
	 * Get widget name.
	 *
	 * @since 1.5.0
	 * @return string
	 */
	public function get_name(): string {
		return 'captwiki-gallery-listing';
	}

	/**
	 * Get widget title.
	 *
	 * @since 1.5.0
	 * @return string
	 */
	public function get_title(): string {
		return esc_html__( 'Gallery Listing', 'captain-widgets-kit' );
	}

	/**
	 * Get widget icon.
	 *
	 * @since 1.5.0
	 * @return string
	 */
	public function get_icon(): string {
		return 'eicon-gallery-grid captwiki-el-logo';
	}

	/**
	 * Get widget categories.
	 *
	 * @since 1.5.0
	 * @return array
	 */
	public function get_categories(): array {
		return array( 'captwiki-essential' );
	}

	/**
	 * Get widget keywords.
	 *
	 * @since 1.5.0
	 * @return array
	 */
	public function get_keywords(): array {
		return array( 'gallery', 'image', 'grid', 'masonry', 'lightbox', 'portfolio', 'media', 'captain-widgets-kit' );
	}

	/**
	 * Common stylesheet dependency.
	 *
	 * @since 1.5.0
	 * @return array
	 */
	public function get_style_depends(): array {
		return array( 'captwiki_gallery_listing' );
	}

	/**
	 * Frontend script dependency.
	 *
	 * @since 1.5.0
	 * @return array
	 */
	public function get_script_depends(): array {
		return array( 'captwiki-gallery-listing' );
	}

	/**
	 * Disable Elementor's default inner wrapper.
	 *
	 * @since 1.5.0
	 * @return bool
	 */
	public function has_widget_inner_wrapper(): bool {
		return ! \Elementor\Plugin::$instance->experiments->is_feature_active( 'e_optimized_markup' );
	}

	/**
	 * Selectable visual styles ( gallery-specific layouts ).
	 *
	 * @since 1.5.0
	 * @return array
	 */
	private function captwiki_style_options(): array {
		return array(
			'style-1' => esc_html__( 'Style 1 – Uniform Grid', 'captain-widgets-kit' ),
			'style-2' => esc_html__( 'Style 2 – Masonry', 'captain-widgets-kit' ),
			'style-3' => esc_html__( 'Style 3 – Hover Overlay', 'captain-widgets-kit' ),
			'style-4' => esc_html__( 'Style 4 – Polaroid Cards', 'captain-widgets-kit' ),
			'style-5' => esc_html__( 'Style 5 – Featured Metro', 'captain-widgets-kit' ),
		);
	}

	/**
	 * Register widget controls.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	protected function register_controls(): void {
		$this->register_gallery_controls();
		$this->register_settings_controls();

		$this->register_layout_style_controls();
		$this->register_overlay_style_controls();
		$this->register_caption_style_controls();
		$this->register_filter_style_controls();
	}

	/**
	 * Content tab — gallery items repeater.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	private function register_gallery_controls(): void {

		$this->start_controls_section(
			'section_gallery',
			array(
				'label' => esc_html__( 'Gallery', 'captain-widgets-kit' ),
				'tab'   => Controls_Manager::TAB_CONTENT,
			)
		);

		$this->add_control(
			'widget_style',
			array(
				'label'   => esc_html__( 'Style', 'captain-widgets-kit' ),
				'type'    => Controls_Manager::SELECT,
				'default' => 'style-1',
				'options' => $this->captwiki_style_options(),
			)
		);

		$item = new Repeater();

		$item->add_control(
			'image',
			array(
				'label'   => esc_html__( 'Image', 'captain-widgets-kit' ),
				'type'    => Controls_Manager::MEDIA,
				'default' => array(
					'url' => Utils::get_placeholder_image_src(),
				),
				'dynamic' => array( 'active' => true ),
			)
		);

		$item->add_control(
			'title',
			array(
				'label'       => esc_html__( 'Title', 'captain-widgets-kit' ),
				'type'        => Controls_Manager::TEXT,
				'default'     => '',
				'label_block' => true,
				'dynamic'     => array( 'active' => true ),
			)
		);

		$item->add_control(
			'caption',
			array(
				'label'       => esc_html__( 'Caption', 'captain-widgets-kit' ),
				'type'        => Controls_Manager::TEXTAREA,
				'rows'        => 2,
				'default'     => '',
				'label_block' => true,
				'dynamic'     => array( 'active' => true ),
			)
		);

		$item->add_control(
			'category',
			array(
				'label'       => esc_html__( 'Filter Category', 'captain-widgets-kit' ),
				'type'        => Controls_Manager::TEXT,
				'default'     => '',
				'label_block' => true,
				'description' => esc_html__( 'Group images for the filter bar ( e.g. "Nature" ). Leave blank to skip.', 'captain-widgets-kit' ),
			)
		);

		$item->add_control(
			'custom_link',
			array(
				'label'         => esc_html__( 'Custom Link', 'captain-widgets-kit' ),
				'type'          => Controls_Manager::URL,
				'placeholder'   => esc_html__( 'https://your-link.com', 'captain-widgets-kit' ),
				'show_external' => true,
				'description'   => esc_html__( 'Used when "Link To" is set to Custom URL.', 'captain-widgets-kit' ),
			)
		);

		$this->add_control(
			'gallery_items',
			array(
				'label'       => esc_html__( 'Images', 'captain-widgets-kit' ),
				'type'        => Controls_Manager::REPEATER,
				'fields'      => $item->get_controls(),
				'default'     => array(
					array(
						'title'    => esc_html__( 'Mountain Trail', 'captain-widgets-kit' ),
						'caption'  => esc_html__( 'Golden hour over the ridge', 'captain-widgets-kit' ),
						'category' => esc_html__( 'Nature', 'captain-widgets-kit' ),
					),
					array(
						'title'    => esc_html__( 'City Lights', 'captain-widgets-kit' ),
						'caption'  => esc_html__( 'Downtown after dark', 'captain-widgets-kit' ),
						'category' => esc_html__( 'Urban', 'captain-widgets-kit' ),
					),
					array(
						'title'    => esc_html__( 'Calm Waters', 'captain-widgets-kit' ),
						'caption'  => esc_html__( 'Reflections on the lake', 'captain-widgets-kit' ),
						'category' => esc_html__( 'Nature', 'captain-widgets-kit' ),
					),
					array(
						'title'    => esc_html__( 'Studio Portrait', 'captain-widgets-kit' ),
						'caption'  => esc_html__( 'Soft light setup', 'captain-widgets-kit' ),
						'category' => esc_html__( 'People', 'captain-widgets-kit' ),
					),
					array(
						'title'    => esc_html__( 'Street Corner', 'captain-widgets-kit' ),
						'caption'  => esc_html__( 'A quiet morning', 'captain-widgets-kit' ),
						'category' => esc_html__( 'Urban', 'captain-widgets-kit' ),
					),
					array(
						'title'    => esc_html__( 'Forest Path', 'captain-widgets-kit' ),
						'caption'  => esc_html__( 'Into the green', 'captain-widgets-kit' ),
						'category' => esc_html__( 'Nature', 'captain-widgets-kit' ),
					),
				),
				'title_field' => '{{{ title || "Image" }}}',
			)
		);

		$this->add_group_control(
			Group_Control_Image_Size::get_type(),
			array(
				'name'    => 'thumbnail',
				'default' => 'large',
				'exclude' => array( 'custom' ),
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Content tab — behaviour & layout settings.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	private function register_settings_controls(): void {

		$this->start_controls_section(
			'section_settings',
			array(
				'label' => esc_html__( 'Settings', 'captain-widgets-kit' ),
				'tab'   => Controls_Manager::TAB_CONTENT,
			)
		);

		$this->add_responsive_control(
			'columns',
			array(
				'label'           => esc_html__( 'Columns', 'captain-widgets-kit' ),
				'type'            => Controls_Manager::NUMBER,
				'min'             => 1,
				'max'             => 6,
				'step'            => 1,
				'default'         => 3,
				'tablet_default'  => 2,
				'mobile_default'  => 1,
				'selectors'       => array(
					'{{WRAPPER}} .captwiki-gallery' => '--captwiki-gallery-cols: {{VALUE}};',
				),
			)
		);

		$this->add_responsive_control(
			'gap',
			array(
				'label'      => esc_html__( 'Spacing', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px', 'em' ),
				'range'      => array(
					'px' => array( 'min' => 0, 'max' => 80 ),
					'em' => array( 'min' => 0, 'max' => 6 ),
				),
				'default'    => array( 'unit' => 'px', 'size' => 16 ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-gallery' => '--captwiki-gallery-gap: {{SIZE}}{{UNIT}};',
				),
			)
		);

		$this->add_control(
			'link_to',
			array(
				'label'   => esc_html__( 'Link To', 'captain-widgets-kit' ),
				'type'    => Controls_Manager::SELECT,
				'default' => 'lightbox',
				'options' => array(
					'lightbox' => esc_html__( 'Lightbox', 'captain-widgets-kit' ),
					'custom'   => esc_html__( 'Custom URL', 'captain-widgets-kit' ),
					'none'     => esc_html__( 'None', 'captain-widgets-kit' ),
				),
			)
		);

		$this->add_control(
			'hover_effect',
			array(
				'label'   => esc_html__( 'Image Hover Effect', 'captain-widgets-kit' ),
				'type'    => Controls_Manager::SELECT,
				'default' => 'zoom-in',
				'options' => array(
					'none'      => esc_html__( 'None', 'captain-widgets-kit' ),
					'zoom-in'   => esc_html__( 'Zoom In', 'captain-widgets-kit' ),
					'zoom-out'  => esc_html__( 'Zoom Out', 'captain-widgets-kit' ),
					'grayscale' => esc_html__( 'Grayscale → Color', 'captain-widgets-kit' ),
					'tilt'      => esc_html__( 'Tilt', 'captain-widgets-kit' ),
				),
			)
		);

		$this->add_control(
			'heading_filter',
			array(
				'label'     => esc_html__( 'Filter Bar', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::HEADING,
				'separator' => 'before',
			)
		);

		$this->add_control(
			'show_filter',
			array(
				'label'        => esc_html__( 'Show Filter Tabs', 'captain-widgets-kit' ),
				'type'         => Controls_Manager::SWITCHER,
				'label_on'     => esc_html__( 'On', 'captain-widgets-kit' ),
				'label_off'    => esc_html__( 'Off', 'captain-widgets-kit' ),
				'return_value' => 'yes',
				'default'      => 'yes',
				'description'  => esc_html__( 'Builds filter buttons from the categories above.', 'captain-widgets-kit' ),
			)
		);

		$this->add_control(
			'all_label',
			array(
				'label'     => esc_html__( '"All" Label', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::TEXT,
				'default'   => esc_html__( 'All', 'captain-widgets-kit' ),
				'condition' => array( 'show_filter' => 'yes' ),
			)
		);

		$this->add_control(
			'heading_caption',
			array(
				'label'     => esc_html__( 'Caption', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::HEADING,
				'separator' => 'before',
			)
		);

		$this->add_control(
			'show_caption',
			array(
				'label'        => esc_html__( 'Show Title & Caption', 'captain-widgets-kit' ),
				'type'         => Controls_Manager::SWITCHER,
				'label_on'     => esc_html__( 'On', 'captain-widgets-kit' ),
				'label_off'    => esc_html__( 'Off', 'captain-widgets-kit' ),
				'return_value' => 'yes',
				'default'      => 'yes',
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Style tab — item / image layout.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	private function register_layout_style_controls(): void {

		$this->start_controls_section(
			'section_layout_style',
			array(
				'label' => esc_html__( 'Items', 'captain-widgets-kit' ),
				'tab'   => Controls_Manager::TAB_STYLE,
			)
		);

		$this->add_control(
			'accent_color',
			array(
				'label'       => esc_html__( 'Accent Color', 'captain-widgets-kit' ),
				'type'        => Controls_Manager::COLOR,
				'default'     => '',
				'selectors'   => array(
					'{{WRAPPER}} .captwiki-gallery' => '--captwiki-gallery-accent: {{VALUE}};',
				),
				'description' => esc_html__( 'Drives the active filter tab and lightbox controls.', 'captain-widgets-kit' ),
			)
		);

		$this->add_responsive_control(
			'item_radius',
			array(
				'label'      => esc_html__( 'Border Radius', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => array( 'px', '%' ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-gallery__media' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				),
			)
		);

		$this->add_group_control(
			Group_Control_Border::get_type(),
			array(
				'name'     => 'item_border',
				'label'    => esc_html__( 'Border', 'captain-widgets-kit' ),
				'selector' => '{{WRAPPER}} .captwiki-gallery__media',
			)
		);

		$this->add_group_control(
			Group_Control_Box_Shadow::get_type(),
			array(
				'name'     => 'item_shadow',
				'label'    => esc_html__( 'Box Shadow', 'captain-widgets-kit' ),
				'selector' => '{{WRAPPER}} .captwiki-gallery__item',
			)
		);

		$this->add_control(
			'image_ratio',
			array(
				'label'       => esc_html__( 'Image Ratio', 'captain-widgets-kit' ),
				'type'        => Controls_Manager::SLIDER,
				'size_units'  => array( '%' ),
				'range'       => array( '%' => array( 'min' => 30, 'max' => 180 ) ),
				'default'     => array( 'unit' => '%', 'size' => 75 ),
				'selectors'   => array(
					'{{WRAPPER}} .captwiki-gallery__media' => '--captwiki-gallery-ratio: {{SIZE}}%;',
				),
				'description' => esc_html__( 'Ignored by the Masonry style ( which keeps original ratios ).', 'captain-widgets-kit' ),
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Style tab — hover overlay.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	private function register_overlay_style_controls(): void {

		$this->start_controls_section(
			'section_overlay_style',
			array(
				'label' => esc_html__( 'Overlay', 'captain-widgets-kit' ),
				'tab'   => Controls_Manager::TAB_STYLE,
			)
		);

		$this->add_control(
			'overlay_color',
			array(
				'label'     => esc_html__( 'Overlay Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__overlay' => 'background-color: {{VALUE}};',
				),
			)
		);

		$this->add_control(
			'overlay_icon_color',
			array(
				'label'     => esc_html__( 'Icon Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__icon' => 'color: {{VALUE}};',
				),
			)
		);

		$this->add_responsive_control(
			'overlay_icon_size',
			array(
				'label'      => esc_html__( 'Icon Size', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px' ),
				'range'      => array( 'px' => array( 'min' => 12, 'max' => 64 ) ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-gallery__icon' => 'font-size: {{SIZE}}{{UNIT}};',
				),
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Style tab — title & caption.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	private function register_caption_style_controls(): void {

		$this->start_controls_section(
			'section_caption_style',
			array(
				'label'     => esc_html__( 'Title & Caption', 'captain-widgets-kit' ),
				'tab'       => Controls_Manager::TAB_STYLE,
				'condition' => array( 'show_caption' => 'yes' ),
			)
		);

		$this->add_control(
			'title_color',
			array(
				'label'     => esc_html__( 'Title Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__title' => 'color: {{VALUE}};',
				),
			)
		);

		$this->add_group_control(
			Group_Control_Typography::get_type(),
			array(
				'name'     => 'title_typography',
				'selector' => '{{WRAPPER}} .captwiki-gallery__title',
			)
		);

		$this->add_control(
			'desc_color',
			array(
				'label'     => esc_html__( 'Caption Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__desc' => 'color: {{VALUE}};',
				),
			)
		);

		$this->add_group_control(
			Group_Control_Typography::get_type(),
			array(
				'name'     => 'desc_typography',
				'selector' => '{{WRAPPER}} .captwiki-gallery__desc',
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Style tab — filter tabs.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	private function register_filter_style_controls(): void {

		$this->start_controls_section(
			'section_filter_style',
			array(
				'label'     => esc_html__( 'Filter Tabs', 'captain-widgets-kit' ),
				'tab'       => Controls_Manager::TAB_STYLE,
				'condition' => array( 'show_filter' => 'yes' ),
			)
		);

		$this->add_group_control(
			Group_Control_Typography::get_type(),
			array(
				'name'     => 'filter_typography',
				'selector' => '{{WRAPPER}} .captwiki-gallery__filter',
			)
		);

		$this->add_responsive_control(
			'filter_gap',
			array(
				'label'      => esc_html__( 'Spacing', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px' ),
				'range'      => array( 'px' => array( 'min' => 0, 'max' => 40 ) ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-gallery__filters' => 'gap: {{SIZE}}{{UNIT}};',
				),
			)
		);

		$this->add_responsive_control(
			'filter_align',
			array(
				'label'     => esc_html__( 'Alignment', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::CHOOSE,
				'default'   => 'center',
				'options'   => array(
					'flex-start' => array(
						'title' => esc_html__( 'Left', 'captain-widgets-kit' ),
						'icon'  => 'eicon-text-align-left',
					),
					'center'     => array(
						'title' => esc_html__( 'Center', 'captain-widgets-kit' ),
						'icon'  => 'eicon-text-align-center',
					),
					'flex-end'   => array(
						'title' => esc_html__( 'Right', 'captain-widgets-kit' ),
						'icon'  => 'eicon-text-align-right',
					),
				),
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__filters' => 'justify-content: {{VALUE}};',
				),
			)
		);

		$this->add_responsive_control(
			'filter_padding',
			array(
				'label'      => esc_html__( 'Padding', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => array( 'px', 'em' ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-gallery__filter' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				),
			)
		);

		$this->add_responsive_control(
			'filter_radius',
			array(
				'label'      => esc_html__( 'Border Radius', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => array( 'px', '%' ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-gallery__filter' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				),
			)
		);

		$this->start_controls_tabs( 'filter_state_tabs' );

		$this->start_controls_tab(
			'filter_normal',
			array( 'label' => esc_html__( 'Normal', 'captain-widgets-kit' ) )
		);

		$this->add_control(
			'filter_color',
			array(
				'label'     => esc_html__( 'Text Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__filter' => 'color: {{VALUE}};',
				),
			)
		);

		$this->add_control(
			'filter_bg',
			array(
				'label'     => esc_html__( 'Background', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__filter' => 'background-color: {{VALUE}};',
				),
			)
		);

		$this->end_controls_tab();

		$this->start_controls_tab(
			'filter_active',
			array( 'label' => esc_html__( 'Active', 'captain-widgets-kit' ) )
		);

		$this->add_control(
			'filter_color_active',
			array(
				'label'     => esc_html__( 'Text Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__filter.is-active' => 'color: {{VALUE}};',
				),
			)
		);

		$this->add_control(
			'filter_bg_active',
			array(
				'label'     => esc_html__( 'Background', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-gallery__filter.is-active' => 'background-color: {{VALUE}};',
				),
			)
		);

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

		$this->end_controls_section();
	}

	/**
	 * Normalise the gallery repeater into a clean, predictable structure.
	 *
	 * @since 1.5.0
	 * @param array  $settings Widget settings.
	 * @param string $size     Chosen image size key.
	 * @return array List of gallery item definitions.
	 */
	private function captwiki_get_items( array $settings, string $size ): array {
		$items = ! empty( $settings['gallery_items'] ) && is_array( $settings['gallery_items'] ) ? $settings['gallery_items'] : array();
		$clean = array();

		foreach ( $items as $item ) {
			$image_id = ! empty( $item['image']['id'] ) ? (int) $item['image']['id'] : 0;
			$thumb    = '';
			$full     = '';

			if ( $image_id ) {
				$thumb = wp_get_attachment_image_url( $image_id, $size );
				$full  = wp_get_attachment_image_url( $image_id, 'full' );
			}

			if ( empty( $thumb ) ) {
				$thumb = ! empty( $item['image']['url'] ) ? $item['image']['url'] : '';
			}
			if ( empty( $full ) ) {
				$full = $thumb;
			}

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

			$category = isset( $item['category'] ) ? trim( (string) $item['category'] ) : '';

			$clean[] = array(
				'image_id' => $image_id,
				'thumb'    => $thumb,
				'full'     => $full,
				'title'    => isset( $item['title'] ) ? (string) $item['title'] : '',
				'caption'  => isset( $item['caption'] ) ? (string) $item['caption'] : '',
				'category' => $category,
				'cat_slug' => '' !== $category ? sanitize_title( $category ) : '',
				'link'     => isset( $item['custom_link'] ) ? $item['custom_link'] : array(),
			);
		}

		return $clean;
	}

	/**
	 * Build the ordered list of unique filter categories.
	 *
	 * @since 1.5.0
	 * @param array $items Cleaned gallery items.
	 * @return array Map of slug => label, preserving first-seen order.
	 */
	private function captwiki_get_filters( array $items ): array {
		$filters = array();

		foreach ( $items as $item ) {
			if ( '' !== $item['cat_slug'] && ! isset( $filters[ $item['cat_slug'] ] ) ) {
				$filters[ $item['cat_slug'] ] = $item['category'];
			}
		}

		return $filters;
	}

	/**
	 * Render the filter tab bar.
	 *
	 * @since 1.5.0
	 * @param array  $filters   Map of slug => label.
	 * @param string $all_label Label for the "show all" tab.
	 * @return void
	 */
	private function captwiki_render_filters( array $filters, string $all_label ): void {
		if ( empty( $filters ) ) {
			return;
		}

		echo '<div class="captwiki-gallery__filters" role="tablist">';

		echo '<button type="button" class="captwiki-gallery__filter is-active" data-filter="*" role="tab" aria-selected="true">' . esc_html( $all_label ) . '</button>';

		foreach ( $filters as $slug => $label ) {
			echo '<button type="button" class="captwiki-gallery__filter" data-filter="' . esc_attr( $slug ) . '" role="tab" aria-selected="false">' . esc_html( $label ) . '</button>';
		}

		echo '</div>';
	}

	/**
	 * Render the image element for one item.
	 *
	 * @since 1.5.0
	 * @param array  $item Cleaned gallery item.
	 * @param string $size Chosen image size key.
	 * @return void
	 */
	private function captwiki_render_image( array $item, string $size ): void {
		$alt = '' !== $item['title'] ? $item['title'] : esc_html__( 'Gallery image', 'captain-widgets-kit' );

		if ( $item['image_id'] ) {
			echo wp_get_attachment_image( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				$item['image_id'],
				$size,
				false,
				array(
					'class'   => 'captwiki-gallery__img',
					'alt'     => $alt,
					'loading' => 'lazy',
				)
			);
			return;
		}

		echo '<img class="captwiki-gallery__img" src="' . esc_url( $item['thumb'] ) . '" alt="' . esc_attr( $alt ) . '" loading="lazy">';
	}

	/**
	 * Render a single gallery item.
	 *
	 * @since 1.5.0
	 * @param array  $item     Cleaned gallery item.
	 * @param array  $settings Widget settings.
	 * @param string $link_to  Link behaviour ( lightbox|custom|none ).
	 * @param string $size     Chosen image size key.
	 * @return void
	 */
	private function captwiki_render_item( array $item, array $settings, string $link_to, string $size ): void {
		$show_caption = 'yes' === $settings['show_caption'];
		$has_meta     = '' !== $item['title'] || '' !== $item['caption'];

		echo '<figure class="captwiki-gallery__item" data-category="' . esc_attr( $item['cat_slug'] ) . '">';

		// Media wrapper — anchor or span depending on link behaviour.
		$is_lightbox = 'lightbox' === $link_to;
		$custom_url  = ( 'custom' === $link_to && ! empty( $item['link']['url'] ) ) ? $item['link']['url'] : '';

		if ( $is_lightbox ) {
			echo '<a class="captwiki-gallery__link" href="' . esc_url( $item['full'] ) . '"'
				. ' data-lightbox="true" data-title="' . esc_attr( $item['title'] ) . '"'
				. ' data-caption="' . esc_attr( $item['caption'] ) . '"'
				. ' aria-label="' . esc_attr( '' !== $item['title'] ? $item['title'] : esc_html__( 'View image', 'captain-widgets-kit' ) ) . '">';
		} elseif ( '' !== $custom_url ) {
			$target = ! empty( $item['link']['is_external'] ) ? ' target="_blank"' : '';
			$rel    = ! empty( $item['link']['nofollow'] ) ? ' rel="nofollow"' : '';
			echo '<a class="captwiki-gallery__link" href="' . esc_url( $custom_url ) . '"' . $target . $rel . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		} else {
			echo '<div class="captwiki-gallery__link">';
		}

		echo '<span class="captwiki-gallery__media">';
		$this->captwiki_render_image( $item, $size );

		echo '<span class="captwiki-gallery__overlay" aria-hidden="true">';
		echo '<span class="captwiki-gallery__icon">';
		echo '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"></circle><path d="M21 21l-4.35-4.35"></path><path d="M11 8v6"></path><path d="M8 11h6"></path></svg>';
		echo '</span>';
		echo '</span>';
		echo '</span>'; // .captwiki-gallery__media

		if ( $show_caption && $has_meta ) {
			echo '<figcaption class="captwiki-gallery__caption">';
			if ( '' !== $item['title'] ) {
				echo '<span class="captwiki-gallery__title">' . esc_html( $item['title'] ) . '</span>';
			}
			if ( '' !== $item['caption'] ) {
				echo '<span class="captwiki-gallery__desc">' . esc_html( $item['caption'] ) . '</span>';
			}
			echo '</figcaption>';
		}

		echo $is_lightbox || '' !== $custom_url ? '</a>' : '</div>';

		echo '</figure>';
	}

	/**
	 * Render widget output on the frontend.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	protected function render(): void {
		$settings = $this->get_settings_for_display();

		$size  = ! empty( $settings['thumbnail_size'] ) ? $settings['thumbnail_size'] : 'large';
		$items = $this->captwiki_get_items( $settings, $size );

		if ( empty( $items ) ) {
			return;
		}

		$allowed_styles = array_keys( $this->captwiki_style_options() );
		$style          = in_array( $settings['widget_style'], $allowed_styles, true ) ? $settings['widget_style'] : 'style-1';

		// Load only the active style stylesheet, depending on the common one.
		wp_enqueue_style(
			'captwiki-gallery-listing-' . $style,
			CAPTWIKI_URL . 'assets/css/widgets/gallery-listing/captwiki-gallery-listing-' . $style . '.css',
			array( 'captwiki_gallery_listing' ),
			CAPTWIKI_VERSION
		);

		$link_to      = in_array( $settings['link_to'], array( 'lightbox', 'custom', 'none' ), true ) ? $settings['link_to'] : 'lightbox';
		$hover        = ! empty( $settings['hover_effect'] ) ? $settings['hover_effect'] : 'zoom-in';
		$show_filter  = 'yes' === $settings['show_filter'];
		$filters      = $show_filter ? $this->captwiki_get_filters( $items ) : array();
		$all_label    = ! empty( $settings['all_label'] ) ? $settings['all_label'] : esc_html__( 'All', 'captain-widgets-kit' );
		$unique       = 'captwiki-widget-' . $this->get_id();

		$wrap_class  = 'captwiki-gallery captwiki-' . $style . ' ' . $unique;
		$wrap_class .= ' captwiki-gallery--hover-' . $hover;
		$wrap_class .= 'yes' === $settings['show_caption'] ? ' captwiki-gallery--has-caption' : '';

		$data_attrs = 'lightbox' === $link_to ? ' data-lightbox="true"' : '';

		echo '<div class="' . esc_attr( $wrap_class ) . '"' . $data_attrs . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		$this->captwiki_render_filters( $filters, $all_label );

		echo '<div class="captwiki-gallery__grid">';

		foreach ( $items as $item ) {
			$this->captwiki_render_item( $item, $settings, $link_to, $size );
		}

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