/*
 * @version: 4.2.0
 * @author: Preline Labs Ltd.
 * @license: Licensed under MIT and Preline UI Fair Use License (https://preline.co/docs/license.html)
 * Copyright 2024 Preline Labs Ltd.
 */

const stringToBoolean = (string: string): boolean => {
	return string === 'true' ? true : false;
};

const getClassProperty = (el: HTMLElement, prop: string, val = '') => {
	return (window.getComputedStyle(el).getPropertyValue(prop) || val).replace(
		' ',
		'',
	);
};

const getClassPropertyAlt = (
	el: HTMLElement,
	prop?: string,
	val: string = '',
) => {
	let targetClass = '';

	el.classList.forEach((c) => {
		if (c.includes(prop)) {
			targetClass = c;
		}
	});

	return targetClass.match(/:(.*)]/) ? targetClass.match(/:(.*)]/)[1] : val;
};

const getZIndex = (el: HTMLElement) => {
	const computedStyle = window.getComputedStyle(el);
	const zIndex = computedStyle.getPropertyValue('z-index');

	return zIndex;
};

const getHighestZIndex = (arr: HTMLElement[]) => {
	let highestZIndex = Number.NEGATIVE_INFINITY;

	arr.forEach((el) => {
		let zIndex: string | number = getZIndex(el);

		if (zIndex !== 'auto') {
			zIndex = parseInt(zIndex, 10);

			if (zIndex > highestZIndex) highestZIndex = zIndex;
		}
	});

	return highestZIndex;
};

const isDirectChild = (parent: Element, child: HTMLElement) => {
	const children = parent.children;

	for (let i = 0; i < children.length; i++) {
		if (children[i] === child) return true;
	}

	return false;
};

const isEnoughSpace = (
	el: HTMLElement,
	toggle: HTMLElement,
	preferredPosition: 'top' | 'bottom' | 'auto' = 'auto',
	space = 10,
	wrapper: HTMLElement | null = null,
) => {
	const referenceRect = toggle.getBoundingClientRect();
	const wrapperRect = wrapper ? wrapper.getBoundingClientRect() : null;
	const viewportHeight = window.innerHeight;
	const spaceAbove = wrapperRect
		? referenceRect.top - wrapperRect.top
		: referenceRect.top;
	const spaceBelow =
		(wrapper ? wrapperRect.bottom : viewportHeight) - referenceRect.bottom;
	const minimumSpaceRequired = el.clientHeight + space;

	if (preferredPosition === 'bottom') {
		return spaceBelow >= minimumSpaceRequired;
	} else if (preferredPosition === 'top') {
		return spaceAbove >= minimumSpaceRequired;
	} else {
		return (
			spaceAbove >= minimumSpaceRequired || spaceBelow >= minimumSpaceRequired
		);
	}
};

const isFocused = (target: HTMLElement) => {
	return document.activeElement === target;
};

const isFormElement = (target: HTMLElement) => {
	return (
		target instanceof HTMLInputElement ||
		target instanceof HTMLTextAreaElement ||
		target instanceof HTMLSelectElement
	);
};

const isIOS = () => {
	if (/iPad|iPhone|iPod/.test(navigator.platform)) {
		return true;
	} else {
		return (
			navigator.maxTouchPoints &&
			navigator.maxTouchPoints > 2 &&
			/MacIntel/.test(navigator.platform)
		);
	}
};

const isIpadOS = () => {
	return (
		navigator.maxTouchPoints &&
		navigator.maxTouchPoints > 2 &&
		/MacIntel/.test(navigator.platform)
	);
};

const isJson = (str: string) => {
	if (typeof str !== 'string') return false;

	const firstChar = str.trim()[0];
	const lastChar = str.trim().slice(-1);

	if (
		(firstChar === '{' && lastChar === '}') ||
		(firstChar === '[' && lastChar === ']')
	) {
		try {
			JSON.parse(str);

			return true;
		} catch {
			return false;
		}
	}

	return false;
};

const isParentOrElementHidden = (element: any): any => {
	if (!element) return false;

	const computedStyle = window.getComputedStyle(element);

	if (computedStyle.display === 'none') return true;

	return isParentOrElementHidden(element.parentElement);
};

const isScrollable = (el: HTMLElement) => {
	const style = window.getComputedStyle(el);
	const overflowY = style.overflowY;
	const overflowX = style.overflowX;
	const canScrollVertically =
		(overflowY === 'scroll' || overflowY === 'auto') &&
		el.scrollHeight > el.clientHeight;
	const canScrollHorizontally =
		(overflowX === 'scroll' || overflowX === 'auto') &&
		el.scrollWidth > el.clientWidth;

	return canScrollVertically || canScrollHorizontally;
};

const debounce = (func: Function, timeout = 200) => {
	let timer: any;

	return (...args: any[]) => {
		clearTimeout(timer);

		timer = setTimeout(() => {
			func.apply(this, args);
		}, timeout);
	};
};

const dispatch = (evt: string, element: any, payload: any = null) => {
	const event = new CustomEvent(evt, {
		detail: { payload },
		bubbles: true,
		cancelable: true,
		composed: false,
	});

	element.dispatchEvent(event);
};

const afterTransition = (el: HTMLElement, callback: Function) => {
	const handleEvent = () => {
		callback();

		el.removeEventListener('transitionend', handleEvent, true);
	};

	const computedStyle = window.getComputedStyle(el);
	const transitionDuration = computedStyle.getPropertyValue(
		'transition-duration',
	);
	const transitionProperty = computedStyle.getPropertyValue(
		'transition-property',
	);
	const hasTransition =
		transitionProperty !== 'none' && parseFloat(transitionDuration) > 0;

	if (hasTransition) el.addEventListener('transitionend', handleEvent, true);
	else callback();
};

const htmlToElement = (html: string): HTMLElement => {
	const template = document.createElement('template');
	html = html.trim();
	template.innerHTML = html;

	return template.content.firstChild as HTMLElement;
};

const classToClassList = (
	classes: string,
	target: HTMLElement,
	splitter = ' ',
	action: 'add' | 'remove' = 'add',
) => {
	const classesToArray = classes.split(splitter);
	classesToArray.forEach((cl) => {
		if (cl.trim()) {
			action === 'add' ? target.classList.add(cl) : target.classList.remove(cl);
		}
	});
};

const menuSearchHistory = {
	historyIndex: -1,

	addHistory(index: number) {
		this.historyIndex = index;
	},

	existsInHistory(index: number) {
		return index > this.historyIndex;
	},

	clearHistory() {
		this.historyIndex = -1;
	},
};

export {
	afterTransition,
	classToClassList,
	debounce,
	dispatch,
	getClassProperty,
	getClassPropertyAlt,
	getHighestZIndex,
	getZIndex,
	htmlToElement,
	isDirectChild,
	isEnoughSpace,
	isFocused,
	isFormElement,
	isIOS,
	isIpadOS,
	isJson,
	isParentOrElementHidden,
	isScrollable,
	menuSearchHistory,
	stringToBoolean,
};
