import { loadStyle } from 'blocksy-frontend'
import ctEvents from 'ct-events'
import { getPastPopups } from './persistence'

import { injectCollectedAssets } from '../../helpers/inject-collected-assets'

import { focusLockManager } from './focus-lock'
import { scrollLockManager } from './scroll-lock'

import { mountAdditionalCloseForPopup } from './popup-additional-close'

let popupStack = []

const getPopupStack = () => popupStack

const preloadAssets = async (el = null) => {
	if (el) {
		const potentialStyles =
			ct_localizations.dynamic_styles_selectors.filter(
				(styleDescriptor) => {
					return (
						el.matches(styleDescriptor.selector) ||
						el.querySelector(styleDescriptor.selector)
					)
				}
			)

		await Promise.all(
			potentialStyles.map((styleDescriptor) =>
				loadStyle(styleDescriptor.url)
			)
		)
	}
}

const onKeydown = (event) => {
	if (event.keyCode !== 27) return
	if (popupStack.length === 0) return

	const topmostPopup = popupStack[popupStack.length - 1]

	let maybeCloseStrategies = {}

	if (topmostPopup.dataset.popupCloseStrategy) {
		maybeCloseStrategies = JSON.parse(
			topmostPopup.dataset.popupCloseStrategy
		)
	}

	if (maybeCloseStrategies.esc) {
		closeMicroPopup(topmostPopup)
	}
}

const addToPopupStack = (popup) => {
	const wasEmpty = popupStack.length === 0

	if (!popupStack.includes(popup)) {
		popupStack.push(popup)
	}

	if (wasEmpty) {
		document.addEventListener('keyup', onKeydown)
	}

	updatePopupZIndexes()
}

const removeFromPopupStack = (popup) => {
	const index = popupStack.indexOf(popup)

	if (index > -1) {
		popupStack.splice(index, 1)
	}

	popup.style.removeProperty('--popup-z-index')

	if (popupStack.length === 0) {
		document.removeEventListener('keyup', onKeydown)
	}

	updatePopupZIndexes()
}

const updatePopupZIndexes = () => {
	popupStack.forEach((popup, index) => {
		if (index === 0) {
			popup.style.removeProperty('--popup-z-index')
		} else {
			popup.style.setProperty('--popup-z-index', index)
		}
	})
}

export const closeMicroPopup = (popup, args = {}) => {
	args = {
		unmount: true,

		// cancel | success
		reason: 'cancel',
		delay: 0,

		...args,
	}

	setTimeout(() => {
		let popupId = popup.id.replace('ct-popup-', '')

		ctEvents.trigger('blocksy:micro-popups:close', popupId, popup)

		localStorage.setItem(
			'blocksyPastPopups',
			JSON.stringify({
				...getPastPopups(),
				[popupId]: {
					...(getPastPopups()[popupId] || {}),
					pages: [location.href],
					closed: {
						reason: args.reason,
						timestamp: new Date().getTime(),
					},
				},
			})
		)

		removeFromPopupStack(popup)

		popup.classList.toggle('active')

		setTimeout(() => {
			const maybeDynamic = popup.querySelector(
				'.ct-dynamic-popup-content'
			)

			if (maybeDynamic) {
				maybeDynamic.remove()
			}
		}, 500)

		if (popup.dataset.focusTrap !== 'no') {
			focusLockManager().focusLockOff()

			// Re-lock focus on previous popup in stack
			if (popupStack.length > 0) {
				const previousPopup = popupStack[popupStack.length - 1]

				if (previousPopup.dataset.focusTrap !== 'no') {
					focusLockManager().focusLockOn(previousPopup)
				}
			}
		}

		if (popup.dataset.scrollLock) {
			scrollLockManager().enable(popup.querySelector('.entry-content'))
		}
		;[...popup.querySelectorAll('iframe[src*="youtu"]')].map((i) => {
			i.contentWindow.postMessage(
				'{"event":"command","func":"stopVideo","args":""}',
				'*'
			)
		})
		;[...popup.querySelectorAll('video')].map((i) => {
			i.pause()
		})
		;[...popup.querySelectorAll('iframe:not([src*="youtu"])')].map((i) => {
			const source = i.src
			i.src = ''
			i.src = source
		})

		if (popup.unmount && args.unmount) {
			popup.unmount()
		}
	}, args.delay)
}

const actuallyOpenPopup = async (popup) => {
	// preloadAssets loads popups.min.css (registered against the .ct-popup
	// selector). Once it's in, drop the always-loaded `hidden` class that kept
	// the shell display:none at rest so it can't flash unstyled.
	await preloadAssets(popup)

	popup.classList.remove('hidden')

	// Wait one frame so the browser paints the at-rest state (opacity:0,
	// display no longer none) before `active` is added below — otherwise the
	// entrance animation snaps instead of transitioning.
	await new Promise((resolve) => requestAnimationFrame(resolve))

	const popupId = popup.id.replace('ct-popup-', '')
	ctEvents.trigger('blocksy:micro-popups:open', popupId, popup)
	ctEvents.trigger('blocksy:frontend:init')

	addToPopupStack(popup)

	popup.classList.add('active')

	localStorage.setItem(
		'blocksyPastPopups',
		JSON.stringify({
			...getPastPopups(),
			[popupId]: {
				...(getPastPopups()[popupId] || {}),
				pages: [...((getPastPopups()[popupId] || {}).pages || [])],
				closed: null,
			},
		})
	)
	if (popup.dataset.focusTrap !== 'no') {
		focusLockManager().focusLockOn(popup)
	}

	if (popup.dataset.scrollLock) {
		scrollLockManager().disable(popup.querySelector('.entry-content'))
	}

	let maybeClose = popup.querySelector('.ct-toggle-close')

	if (maybeClose && !maybeClose.hasClickListener) {
		maybeClose.hasClickListener = true

		popup
			.querySelector('.ct-toggle-close')
			.addEventListener('click', (e) => {
				e.preventDefault()
				closeMicroPopup(popup)
			})
	}

	let maybeCloseStrategies = {}

	if (popup.dataset.popupCloseStrategy) {
		maybeCloseStrategies = JSON.parse(popup.dataset.popupCloseStrategy)
	}

	if (maybeCloseStrategies.backdrop) {
		if (popup.dataset.popupBackdrop === 'yes' && !popup.hasClickListener) {
			popup.hasClickListener = true
			popup.addEventListener('click', (e) => {
				if (e.target.dataset.popupBackdrop === 'yes') {
					e.preventDefault()
					closeMicroPopup(popup)
				}
			})
		}
	}

	if (maybeCloseStrategies.form_submit || maybeCloseStrategies.button_click) {
		mountAdditionalCloseForPopup(popup, { maybeCloseStrategies })
	}
}

let controller = null

export const openMicroPopup = (popup, payload = {}) => {
	let isOpen = popup.classList.contains('active')

	if (isOpen) {
		closeMicroPopup(popup)
		return
	}

	if (!popup.querySelector('.ct-popup-content')) {
		const receivedPopupContent = async (content, assets) => {
			if (assets) {
				await injectCollectedAssets(assets)
			}

			const tempContainer = document.createElement('div')
			tempContainer.innerHTML = content

			if (tempContainer.querySelector('.entry-content')) {
				popup
					.querySelector('article')
					.appendChild(tempContainer.querySelector('.entry-content'))

				popup
					.querySelector('article')
					.lastElementChild.classList.add('ct-dynamic-popup-content')
			}

			actuallyOpenPopup(popup)
		}

		const popupId = popup.id.replace('ct-popup-', '')

		const persistedPopup = getPastPopups()[popupId]

		const popupCacheKey = popup.dataset.cacheKey

		if (
			popupCacheKey &&
			persistedPopup &&
			persistedPopup.content &&
			persistedPopup.content.cacheKey === popupCacheKey
		) {
			receivedPopupContent(
				persistedPopup.content.content,
				persistedPopup.content.assets
			)
			return
		}

		const body = new FormData()

		let maybePostId = payload.postId

		if (!maybePostId) {
			const maybeClass = [...document.body.classList].find(
				(c) => c.indexOf('postid-') === 0
			)

			if (maybeClass) {
				maybePostId = maybeClass.replace('postid-', '')
			}
		}

		body.append('popup_id', popupId)
		if (maybePostId) {
			body.append('post_id', maybePostId)
		}

		if (controller) {
			controller.abort()
		}

		if ('AbortController' in window) {
			controller = new AbortController()
		}

		fetch(
			`${ct_localizations.ajax_url}?action=blc_retrieve_popup_content`,
			{
				method: 'POST',
				body,
				signal: controller.signal,
			}
		)
			.then((response) => response.json())
			.then(({ success, data }) => {
				if (!success) {
					return
				}

				if (!data || !data.content) {
					return
				}

				if (popupCacheKey) {
					// Only cache assets not already on the page
					const newAssets = data.assets ? {
						styles: (data.assets.styles || []).filter(
							({ handle }) => !document.getElementById(`${handle}-css`)
						),
						scripts: (data.assets.scripts || []).filter(
							({ handle }) => !document.getElementById(`${handle}-js`)
						),
						...(data.assets.inline_css ? { inline_css: data.assets.inline_css } : {})
					} : null

					localStorage.setItem(
						'blocksyPastPopups',
						JSON.stringify({
							...getPastPopups(),
							[popupId]: {
								...(getPastPopups()[popupId] || {}),
								content: {
									content: data.content,
									assets: newAssets,
									cacheKey: popupCacheKey,
								},
							},
						})
					)
				}

				receivedPopupContent(data.content, data.assets)
			})

		return
	}

	actuallyOpenPopup(popup)
}
