// @jsx h
import { h } from 'dom-chef'
import classnames from 'classnames'

import { loadStyle } from '../helpers'
import { isIosDevice } from './helpers/is-ios-device'
import { whenTransitionEnds } from './helpers/when-transition-ends'

let alreadyRunning = false

const decodeHTMLEntities = (string) => {
	var doc = new DOMParser().parseFromString(string, 'text/html')
	return doc.documentElement.textContent
}

const store = {}
let controller = null

const cachedFetch = (url, nonce = '') => {
	if (controller) {
		controller.abort()
		controller = null
	}

	if (store[url]) {
		return new Promise((resolve) => {
			resolve(store[url])
			store[url] = store[url].clone()
		})
	}

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

	return fetch(url, {
		signal: controller.signal,
		headers: {
			'X-WP-Nonce': nonce,
		},
	}).then((response) => {
		store[url] = response.clone()

		controller = null

		return response
	})
}

const debounce = (fn, wait) => {
	var timeout
	return function () {
		if (!wait) {
			return fn.apply(this, arguments)
		}
		var context = this
		var args = arguments
		clearTimeout(timeout)
		timeout = setTimeout(function () {
			timeout = null
			return fn.apply(context, args)
		}, wait)
	}
}

const getPreviewElFor = ({
	hasThumbs,
	post: {
		title: { rendered },
		link: href,
		_embedded = {},
		product_price = 0,
		product_status = '',
		placeholder_image = null,
	},
}) => {
	const decodedTitle = decodeHTMLEntities(rendered)

	const defaultMediaDetails = {
		sizes: {
			thumbnail: {
				source_url: placeholder_image,
			},
		},
	}

	const sizes =
		(
			_embedded['wp:featuredmedia']?.[0]?.media_details ||
			defaultMediaDetails
		).sizes || {}

	return (
		<a className="ct-search-item" role="option" key={href} {...{ href }}>
			{(_embedded['wp:featuredmedia'] || placeholder_image) &&
				hasThumbs && (
					<span
						{...{
							class: classnames({
								['ct-media-container']: true,
							}),
						}}>
						<img
							{...{
								src: sizes.thumbnail
									? sizes?.thumbnail.source_url
									: values(sizes).reduce(
											(currentSmallest, current) =>
												current.width <
												currentSmallest.width
													? current
													: currentSmallest,
											{
												width: 9999999999,
											}
									  ).source_url ||
									  _embedded['wp:featuredmedia'][0]
											.source_url,
							}}
							style={{ aspectRatio: '1/1' }}
						/>
					</span>
				)}
			<span>
				{decodedTitle}

				{product_price || product_status ? (
					<span className="product-search-meta">
						{product_price ? (
							<small
								className="price"
								dangerouslySetInnerHTML={{
									__html: product_price,
								}}
								key="price"
							/>
						) : null}
						{product_status ? (
							<small
								className="stock-status"
								dangerouslySetInnerHTML={{
									__html: product_status,
								}}
								key="product-status"
							/>
						) : null}
					</span>
				) : null}
			</span>
		</a>
	)
}

export const mount = (formEl, args = {}) => {
	const clickOutsideHandler = (e) => {
		let mode = { mode: 'inline', ...args }.mode

		if (mode === 'modal') {
			return
		}

		if (formEl.contains(e.target)) {
			return
		}

		fadeOutAndRemove(formEl.querySelector('.ct-search-results'))
	}

	const maybeEl = formEl.querySelector('input[type="search"]')

	const options = {
		postType: 'ct_forced_any',

		// inline | modal
		mode: 'inline',

		perPage: 5,

		...args,
	}

	if (!maybeEl) {
		return
	}

	options.postType = formEl.querySelector('[name="post_type"]')
		? `ct_forced_${formEl.querySelector('[name="post_type"]').value}`
		: formEl.querySelector('[name="ct_post_type"]')
		? `ct_forced_${formEl.querySelector('[name="ct_post_type"]').value}`
		: 'ct_forced_any'

	if (options.postType.includes(':')) {
		options.postType.replace('ct_forced_', '')
	}

	options.ctSearchTaxonomies =
		formEl.querySelector('[name="ct_search_taxonomies"]')?.value || ''

	options.productPrice = !!formEl.closest(
		'[data-live-results*="product_price"]'
	)

	options.productStatus = !!formEl.closest(
		'[data-live-results*="product_status"]'
	)

	if (!window.fetch) return

	let listener = debounce((e) => {
		document.removeEventListener('click', clickOutsideHandler)
		document.addEventListener('click', clickOutsideHandler)

		if (e.target.value.trim().length === 0) {
			fadeOutAndRemove(formEl.querySelector('.ct-search-results'))

			let maybeStatusEl = formEl.querySelector('[aria-live]')

			if (maybeStatusEl) {
				maybeStatusEl.innerHTML = ct_localizations.search_live_no_result
			}

			return
		}

		options.queryCategory = formEl.querySelector('[name="ct_tax_query"]')
			? formEl.querySelector('[name="ct_tax_query"]').value
			: ''

		formEl.classList.add('ct-searching')

		const params = new URLSearchParams()
		params.append('_embed', '1')
		params.append('post_type', options.postType)
		params.append('per_page', options.perPage)

		if (options.productPrice === 'true' || options.productPrice === true) {
			params.append('product_price', options.productPrice)
		}

		if (
			options.productStatus === 'true' ||
			options.productStatus === true
		) {
			params.append('product_status', options.productStatus)
		}

		if (options.queryCategory) {
			params.append('ct_tax_query', options.queryCategory)
		}

		if (options.ctSearchTaxonomies) {
			params.append('ct_search_taxonomies', options.ctSearchTaxonomies)
		}

		if (ct_localizations.lang) {
			params.append('lang', ct_localizations.lang)
		}

		params.append('search', e.target.value)

		cachedFetch(
			`${ct_localizations.rest_url}wp/v2/posts${
				ct_localizations.rest_url.indexOf('?') > -1 ? '&' : '?'
			}${params.toString()}`,

			formEl.querySelector('.ct-live-results-nonce')
				? formEl.querySelector('.ct-live-results-nonce').value
				: ''
		)
			.then((response) => {
				let totalAmountOfPosts = parseInt(
					response.headers.get('X-WP-Total'),
					10
				)

				loadStyle(ct_localizations.dynamic_styles.search_lazy).then(
					() => {
						response
							.json()
							.then((posts) => {
								if (alreadyRunning) {
									return
								}

								formEl.classList.remove('ct-searching')

								let itHadSearchResultsBefore =
									!!formEl.querySelector('.ct-search-results')

								alreadyRunning = true

								let searchResults =
									formEl.querySelector('.ct-search-results')

								let { height: heightBeforeRemoval } =
									searchResults
										? searchResults.getBoundingClientRect()
										: 0

								if (
									searchResults &&
									!(
										e.target.value.trim().length === 0 ||
										posts.length === 0
									)
								) {
									/**
									 * Should just quickly replace the list
									 * when results are available
									 */
									searchResults &&
										formEl.removeChild(searchResults)
								} else {
									if (
										e.target.value.trim().length === 0 ||
										posts.length === 0
									) {
										fadeOutAndRemove(searchResults)
									}
								}

								let searchResultsCountElLabel =
									ct_localizations.search_live_no_result

								if (
									posts.length > 0 &&
									e.target.value.trim().length > 0
								) {
									searchResultsCountElLabel = (
										posts.length > 1
											? ct_localizations.search_live_many_results
											: ct_localizations.search_live_one_result
									).replace('%s', posts.length)
								}

								let maybeStatusEl =
									formEl.querySelector('[aria-live]')

								if (maybeStatusEl) {
									maybeStatusEl.innerHTML =
										searchResultsCountElLabel
								}

								if (
									posts.length > 0 &&
									e.target.value.trim().length > 0
								) {
									let searchResultsEl = (
										<div
											class="ct-search-results"
											role="listbox"
											aria-label={
												ct_localizations.search_live_results
											}>
											{posts
												.filter((post) => post?.id)
												.map((post) =>
													getPreviewElFor({
														post,
														hasThumbs:
															(
																formEl.dataset
																	.liveResults ||
																''
															).indexOf(
																'thumbs'
															) > -1,
													})
												)}

											{totalAmountOfPosts >
											options.perPage ? (
												<a
													className="ct-search-more"
													{...{
														href: ct_localizations.search_url.replace(
															/QUERY_STRING/,
															e.target.value
														),
													}}>
													{
														ct_localizations.show_more_text
													}
												</a>
											) : (
												[]
											)}
										</div>
									)

									formEl.appendChild(searchResultsEl)

									if (!itHadSearchResultsBefore) {
										fadeIn(
											formEl.querySelector(
												'.ct-search-results'
											)
										)
									} else {
										let searchResults =
											formEl.querySelector(
												'.ct-search-results'
											)

										let { height: heightAfterReplace } =
											searchResults.getBoundingClientRect()

										if (
											heightBeforeRemoval !==
											heightAfterReplace
										) {
											searchResults.style.height = `${heightBeforeRemoval}px`
											searchResults.classList.add(
												'ct-slide'
											)

											requestAnimationFrame(() => {
												searchResults.style.height = `${heightAfterReplace}px`

												whenTransitionEnds(
													searchResults,
													() => {
														searchResults.removeAttribute(
															'style'
														)

														searchResults.classList.remove(
															'ct-slide'
														)
													}
												)
											})
										}
									}

									if (
										formEl.querySelector('.ct-search-more')
									) {
										formEl
											.querySelector('.ct-search-more')
											.addEventListener('click', (e) => {
												e.preventDefault()
												formEl.submit()
											})
									}

									if (isIosDevice()) {
										window.scrollTo(0, 0)
									}
								}

								alreadyRunning = false
							})
							.catch((error) => {})
					}
				)
			})
			.catch((error) => {})
	}, 300)

	maybeEl.addEventListener('input', listener)

	maybeEl.addEventListener('keydown', (e) => {
		if (e.key === 'Escape') {
			e.preventDefault()
		}
	})

	maybeEl.addEventListener('focus', (e) => {
		listener(e)
	})

	if (maybeEl.value.length > 0) {
		listener({ target: maybeEl })
	}
}

function fadeOutAndRemove(el) {
	if (!el) return

	let { height } = el.getBoundingClientRect()

	el.classList.add('ct-fade-leave')
	el.style.height = `${height}px`

	el.closest('form').classList.remove('ct-has-dropdown')

	requestAnimationFrame(() => {
		el.classList.remove('ct-fade-leave')
		el.classList.add('ct-fade-leave-active')
		el.style.height = 0

		whenTransitionEnds(
			el,
			() => el.parentNode && el.parentNode.removeChild(el)
		)
	})
}

function fadeIn(el) {
	el.classList.add('ct-fade-enter')

	let { height } = el.getBoundingClientRect()

	el.classList.add('ct-fade-leave')
	el.style.height = 0

	el.closest('form').classList.add('ct-has-dropdown')

	requestAnimationFrame(() => {
		el.style.height = `${height}px`
		el.classList.remove('ct-fade-enter')
		el.classList.add('ct-fade-enter-active')

		whenTransitionEnds(el, () => el.removeAttribute('style'))
	})
}

function values(obj) {
	var result = []

	if (typeof obj == 'object' || typeof obj == 'function') {
		var keys = Object.keys(obj)
		var len = keys.length

		for (var i = 0; i < len; i++) {
			result.push(obj[keys[i]])
		}

		return result
	}
}
