import $ from 'jquery';

const { VPData } = window;
const { settingsPopupGallery } = VPData;
const templatesSupport = 'content' in document.createElement( 'template' );

/*
 * Global Popup Gallery API.
 */
const VPPopupAPI = {
	vendor: false,

	vendors: [
		{
			vendor: 'youtube',
			embedUrl: 'https://www.youtube.com/embed/{{video_id}}?{{params}}',
			pattern:
				/(https?:\/\/)?(www.)?(youtube\.com|youtu\.be|youtube-nocookie\.com)\/(?:embed\/|shorts\/|v\/|watch\?v=|watch\?list=(.*)&v=|watch\?(.*[^&]&)v=)?((\w|-){11})(&list=(\w+)&?)?(.*)/,
			patternIndex: 6,
			params: {
				autoplay: 1,
				autohide: 1,
				fs: 1,
				rel: 0,
				hd: 1,
				wmode: 'transparent',
				enablejsapi: 1,
				html5: 1,
			},
			paramsIndex: 10,
			embedCallback( url, match ) {
				let result = false;
				const vendorData = this;
				const videoId =
					match && match[ vendorData.patternIndex ]
						? match[ vendorData.patternIndex ]
						: false;

				if ( videoId ) {
					const isShorts = /\/shorts\//.test( url );

					const width = isShorts ? 476 : 1920;
					const height = isShorts ? 847 : 1080;

					result = VPPopupAPI.embedCallback(
						{
							...vendorData,
							width,
							height,
						},
						videoId,
						url,
						match
					);
				}

				return result;
			},
		},
		{
			vendor: 'vimeo',
			embedUrl: 'https://player.vimeo.com/video/{{video_id}}?{{params}}',
			pattern:
				/https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)(?:$|\/|\?)(.*)/,
			patternIndex: 3,
			params: {
				autoplay: 1,
				hd: 1,
				show_title: 1,
				show_byline: 1,
				show_portrait: 0,
				fullscreen: 1,
			},
			paramsIndex: 4,
		},
	],

	init() {},
	open() {},
	close() {},

	/**
	 * Parse query parameters.
	 * Thanks to https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
	 *
	 * @param {string} query - query string.
	 *
	 * @return {string}
	 */
	getQueryStringParams( query ) {
		return query
			? ( /^[?#]/.test( query ) ? query.slice( 1 ) : query )
					.split( '&' )
					.reduce( ( params, param ) => {
						const [ key, value ] = param.split( '=' );
						params[ key ] = value
							? decodeURIComponent( value.replace( /\+/g, ' ' ) )
							: '';
						return params;
					}, {} )
			: {};
	},

	/**
	 * Prepare params from parsed URL.
	 *
	 * @param {Object} match      - url match data.
	 * @param {Object} vendorData - vendor data.
	 *
	 * @return {string}
	 */
	prepareParams( match, vendorData ) {
		let result = '';

		// Prepare default params.
		const params = vendorData.params || {};

		// Parse params from URL.
		if (
			vendorData.paramsIndex &&
			match &&
			match[ vendorData.paramsIndex ]
		) {
			const newParams = VPPopupAPI.getQueryStringParams(
				match[ vendorData.paramsIndex ]
			);

			if ( newParams && typeof newParams === 'object' ) {
				Object.keys( newParams ).forEach( ( key ) => {
					if ( key && newParams[ key ] ) {
						params[ key ] = newParams[ key ];
					}
				} );
			}
		}

		if ( params && Object.keys( params ).length ) {
			Object.keys( params ).forEach( ( key ) => {
				if ( key && params[ key ] ) {
					if ( result ) {
						result += '&';
					}
					result += `${ key }=${ params[ key ] }`;
				}
			} );
		}

		return result;
	},

	/**
	 * Prepare data for embed.
	 *
	 * @param {Object}           vendorData current video vendor data.
	 * @param {string}           videoId    parsed video ID.
	 * @param {string}           url        video URL provided.
	 * @param {Object | boolean} match      URL match data.
	 *
	 * @return {Object}
	 */
	embedCallback( vendorData, videoId, url, match = false ) {
		let { embedUrl } = vendorData;
		embedUrl = embedUrl.replace( /{{video_id}}/g, videoId );
		embedUrl = embedUrl.replace( /{{video_url}}/g, url );
		embedUrl = embedUrl.replace(
			/{{video_url_encoded}}/g,
			encodeURIComponent( url )
		);
		embedUrl = embedUrl.replace(
			/{{params}}/g,
			match ? VPPopupAPI.prepareParams( match, vendorData ) : ''
		);

		const width = vendorData.width || 1920;
		const height = vendorData.height || 1080;

		return {
			vendor: vendorData.vendor,
			id: videoId,
			embed: `<iframe width="${ width }" height="${ height }" src="${ embedUrl }" scrolling="no" frameborder="0" allowTransparency="true" allow="accelerometer; autoplay; clipboard-write; fullscreen; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`,
			embedUrl,
			url,
			width,
			height,
		};
	},

	/**
	 * Parse video URL and return object with data
	 *
	 * @param {string} url    - video url.
	 * @param {string} url    - optional poster url.
	 *
	 * @param          poster
	 * @return {object|boolean} video data
	 */
	parseVideo( url, poster ) {
		let result = false;

		VPPopupAPI.vendors.forEach( ( vendorData ) => {
			if ( ! result ) {
				const match = url.match( vendorData.pattern );
				const videoId =
					match && match[ vendorData.patternIndex ]
						? match[ vendorData.patternIndex ]
						: false;

				if ( videoId ) {
					// Custom embed callback.
					if ( vendorData.embedCallback ) {
						result = vendorData.embedCallback( url, match, poster );

						// Predefined embed callback.
					} else {
						result = VPPopupAPI.embedCallback(
							vendorData,
							videoId,
							url,
							match
						);
					}
				}
			}
		} );

		// Unknown vendor.
		if ( ! result ) {
			result = VPPopupAPI.embedCallback(
				{
					vendor: 'unknown',
					embedUrl: url,
				},
				url,
				url,
				false
			);
		}

		return result;
	},

	/**
	 * Parse gallery item popup data.
	 *
	 * @param {element} itemElement - gallery item
	 */
	parseItem( itemElement ) {
		let result = false;

		const $dataElement =
			itemElement &&
			itemElement.querySelector( '.vp-portfolio__item-popup' );

		if ( $dataElement ) {
			result = {
				$dataElement,
				$content: $dataElement,
				data: $dataElement.dataset,
			};

			// Support for <template> tag.
			if (
				templatesSupport &&
				$dataElement.nodeName === 'TEMPLATE' &&
				$dataElement.content
			) {
				result.$content = $dataElement.content;
			}

			result.$title = result?.$content?.querySelector(
				'.vp-portfolio__item-popup-title'
			);
			result.$description = result?.$content?.querySelector(
				'.vp-portfolio__item-popup-description'
			);
		}

		return result;
	},

	/**
	 * Parse gallery
	 *
	 * @param {jQuery} $gallery - gallery element.
	 *
	 * @return {Array} gallery data
	 */
	parseGallery( $gallery ) {
		const items = [];
		let size;
		let item;
		let video;
		let videoData;

		// Find all gallery items
		// Skip Swiper slider duplicates.
		// Previously we also used the `:not(.swiper-slide-duplicate-active)`, but it contains a valid first slide.
		$gallery
			.find( '.vp-portfolio__item-wrap:not(.swiper-slide-duplicate)' )
			.each( function () {
				const itemData = VPPopupAPI.parseItem( this );

				if ( itemData ) {
					size = (
						itemData?.data?.vpPopupImgSize || '1920x1080'
					).split( 'x' );
					video = itemData?.data?.vpPopupVideo;
					videoData = false;

					if ( video ) {
						videoData = VPPopupAPI.parseVideo(
							video,
							itemData?.data?.vpPopupPoster
						);
					}

					if ( videoData ) {
						item = {
							type: 'embed',
							el: this,
							poster: videoData.poster,
							src: videoData.embedUrl,
							embed: videoData.embed,
							width: videoData.width || 1920,
							height: videoData.height || 1080,
						};
					} else {
						// create slide object
						item = {
							type: 'image',
							el: this,
							src: itemData?.data?.vpPopupImg,
							srcset: itemData?.data?.vpPopupImgSrcset,
							width: parseInt( size[ 0 ], 10 ),
							height: parseInt( size[ 1 ], 10 ),
						};

						const srcSmall =
							itemData?.data?.vpPopupSmImg || item.src;
						if ( srcSmall ) {
							const smallSize = (
								itemData?.data?.vpPopupSmImgSize ||
								itemData?.data?.vpPopupImgSize ||
								'1920x1080'
							).split( 'x' );

							item.srcSmall = srcSmall;
							item.srcSmallWidth = parseInt( smallSize[ 0 ], 10 );
							item.srcSmallHeight = parseInt(
								smallSize[ 1 ],
								10
							);
						}

						const srcMedium =
							itemData?.data?.vpPopupMdImg || item.src;
						if ( srcMedium ) {
							const mediumSize = (
								itemData?.data?.vpPopupMdImgSize ||
								itemData?.data?.vpPopupImgSize ||
								'1920x1080'
							).split( 'x' );

							item.srcMedium = srcMedium;
							item.srcMediumWidth = parseInt(
								mediumSize[ 0 ],
								10
							);
							item.srcMediumHeight = parseInt(
								mediumSize[ 1 ],
								10
							);
						}
					}

					if ( itemData?.$title || itemData?.$description ) {
						item.caption =
							( itemData?.$title?.outerHTML || '' ) +
							( itemData?.$description?.outerHTML || '' );
					}

					items.push( item );
				}
			} );

		return items;
	},

	/**
	 * Try to focus gallery item link.
	 * Used when popup gallery is closed.
	 *
	 * @param {Object} data - data of the current item
	 */
	maybeFocusGalleryItem( data ) {
		if ( ! settingsPopupGallery.restore_focus ) {
			return;
		}

		// Focus native gallery item.
		if ( data.linkEl ) {
			$( data.linkEl ).focus();

			// Focus Visual Portfolio gallery item.
		} else if ( data.el ) {
			$( data.el ).find( '.vp-portfolio__item-img > a' ).focus();
		}
	},

	/**
	 * Emit popup event with fallback for non-Visual Portfolio galleries
	 * Uses self.emitEvent() when available, otherwise triggers document event manually
	 *
	 * @param {string}        eventName - Event name (e.g., 'initFancybox')
	 * @param {Array}         eventArgs - Event arguments array
	 * @param {Object | null} self      - Gallery instance (null for 3rd-party)
	 */
	emitEvent( eventName, eventArgs, self ) {
		if ( self && self.emitEvent ) {
			// Use instance method for Visual Portfolio galleries
			self.emitEvent( eventName, eventArgs );
		} else {
			// Manually trigger event for 3rd-party galleries
			const globalEventArgs = [ null ].concat( eventArgs );
			$( document ).trigger( `${ eventName }.vpf`, globalEventArgs );
		}
	},
};

window.VPPopupAPI = VPPopupAPI;

// Extend VP class.
$( document ).on( 'extendClass.vpf', ( event, VP ) => {
	if ( event.namespace !== 'vpf' ) {
		return;
	}

	/**
	 * Init popup gallery
	 */
	VP.prototype.initPopupGallery = function () {
		const self = this;
		if (
			! self.options.itemsClickAction ||
			self.options.itemsClickAction === 'url'
		) {
			return;
		}

		// prevent on preview page
		if ( self.isPreview() ) {
			return;
		}

		// click action
		// `a.vp-portfolio__item-overlay` added as fallback for old templates, used in themes.
		self.$item.on(
			`click.vpf-uid-${ self.uid }`,
			`
        .vp-portfolio__item a.vp-portfolio__item-meta,
        .vp-portfolio__item .vp-portfolio__item-img > a,
        .vp-portfolio__item .vp-portfolio__item-meta-title > a,
        .vp-portfolio__item a.vp-portfolio__item-overlay
      `,
			function ( e ) {
				if ( e.isDefaultPrevented() ) {
					return;
				}

				const $this = $( this );
				let $itemWrap = $this.closest( '.vp-portfolio__item-wrap' );

				// Use Swiper data-attribute to support slide duplicates.
				if (
					$itemWrap.hasClass( 'swiper-slide-duplicate' ) &&
					$itemWrap.attr( 'data-swiper-slide-index' )
				) {
					$itemWrap = self.$item.find(
						`[data-swiper-slide-index="${ $itemWrap.attr(
							'data-swiper-slide-index'
						) }"].swiper-slide:not(.swiper-slide-duplicate)`
					);
				}

				if ( ! $itemWrap.find( '.vp-portfolio__item-popup' ).length ) {
					return;
				}

				const items = VPPopupAPI.parseGallery( self.$item );
				let index = -1;

				// Get gallery item index.
				// We should check all items with gallery data to prevent
				// issue with items and custom URL used.
				items.forEach( ( item, idx ) => {
					if ( item.el === $itemWrap[ 0 ] ) {
						index = idx;
					}
				} );

				// Let's open popup once item index found.
				if ( index !== -1 ) {
					e.preventDefault();
					VPPopupAPI.open( items, index, self );
				}
			}
		);
	};

	/**
	 * Destroy popup gallery
	 */
	VP.prototype.destroyPopupGallery = function () {
		const self = this;

		if (
			! self.options.itemsClickAction ||
			self.options.itemsClickAction === 'url'
		) {
			return;
		}

		self.$item.off( `click.vpf-uid-${ self.uid }` );

		self.emitEvent( 'destroyPopupGallery' );
	};
} );

// Init.
$( document ).on( 'init.vpf', ( event, self ) => {
	if ( event.namespace !== 'vpf' ) {
		return;
	}

	self.initPopupGallery();
} );

// Destroy.
$( document ).on( 'destroy.vpf', ( event, self ) => {
	if ( event.namespace !== 'vpf' ) {
		return;
	}

	self.destroyPopupGallery();
} );

// Check if link is image.
function isLinkImage( link ) {
	return /(.png|.jpg|.jpeg|.gif|.tiff|.tif|.jfif|.jpe|.svg|.bmp|.webp)$/.test(
		link.href.toLowerCase().split( '?' )[ 0 ].split( '#' )[ 0 ]
	);
}

// Parse image data from link.
function parseImgData( link ) {
	const $link = $( link );
	let img = link.childNodes[ 0 ];
	let caption = $link.next( 'figcaption' );

	// <noscript> tag used in plugins, that adds lazy loading
	if ( img.nodeName === 'NOSCRIPT' && link.childNodes[ 1 ] ) {
		img = link.childNodes[ 1 ];
	}

	if ( ! caption.length && $link.parent( '.gallery-icon' ).length ) {
		caption = $link.parent( '.gallery-icon' ).next( 'figcaption' );
	}

	caption = caption.html();

	if ( caption ) {
		caption = `<div class="vp-portfolio__item-popup-description">${ caption }</div>`;
	}

	return {
		type: 'image',
		el: img,
		linkEl: link,
		src: link.href,
		caption,
	};
}

/**
 * Popup for WordPress images and custom galleries
 *
 * This code is active when the "Lightbox for WordPress Images" setting is enabled.
 * It handles:
 * - WordPress native galleries (blocks, classic galleries, Jetpack)
 * - WordPress single images in content
 * - Custom galleries marked with .vp-lightbox-gallery class
 *
 * Custom gallery usage:
 * - Single image: <a href="image.jpg" class="vp-lightbox-gallery"><img /></a>
 * - Gallery: <div class="vp-lightbox-gallery"><a href="img1.jpg">...</a></div>
 */
if ( settingsPopupGallery.enable_on_wordpress_images ) {
	$( document ).on(
		'click',
		`
			.wp-block-image > a,
			.wp-block-image > figure > a,
			.wp-block-gallery .blocks-gallery-item > figure > a,
			.wp-block-gallery .wp-block-image > a,
			.wp-block-media-text > figure > a,
			.gallery .gallery-icon > a,
			figure.wp-caption > a,
			figure.tiled-gallery__item > a,
			p > a,
			a.vp-lightbox-gallery,
			.vp-lightbox-gallery a
		`,
		function ( e ) {
			if ( e.isDefaultPrevented() ) {
				return;
			}

			if ( ! this.childNodes.length ) {
				return;
			}

			let imageNode = this.childNodes[ 0 ];

			// <noscript> tag used in plugins, that adds lazy loading
			if ( imageNode.nodeName === 'NOSCRIPT' && this.childNodes[ 1 ] ) {
				imageNode = this.childNodes[ 1 ];
			}

			// check if child node is <img> or <picture> tag.
			// <picture> tag used in plugins, that adds WebP support
			if (
				imageNode.nodeName !== 'IMG' &&
				imageNode.nodeName !== 'PICTURE'
			) {
				return;
			}

			// check if link is image.
			if ( ! isLinkImage( this ) ) {
				return;
			}

			e.preventDefault();

			const $this = $( this );
			const items = [];
			const currentImage = parseImgData( this );
			const $gallery = $this.closest(
				'.wp-block-gallery, .gallery, .tiled-gallery__gallery, .vp-lightbox-gallery'
			);
			let activeIndex = 0;

			// Block gallery, WordPress default gallery, Jetpack gallery, custom gallery.
			if ( $gallery.length ) {
				// Check if it's a custom gallery or WordPress gallery
				const isCustomGallery = $gallery.hasClass(
					'vp-lightbox-gallery'
				);
				const $galleryItems = isCustomGallery
					? $gallery.find( 'a' )
					: $gallery.find(
							`
								.blocks-gallery-item > figure > a,
								.wp-block-image > a,
								.gallery-icon > a,
								figure.tiled-gallery__item > a
							`
					  );

				let i = 0;

				$galleryItems.each( function () {
					// check if link is image.
					if ( isLinkImage( this ) ) {
						if ( this === currentImage.linkEl ) {
							activeIndex = i;
						}

						items.push( parseImgData( this ) );

						i += 1;
					}
				} );

				// WordPress gallery.
			} else {
				items.push( currentImage );
			}

			VPPopupAPI.open( items, activeIndex );
		}
	);
}
