/*
 * HSOverlay
 * @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.
 */

import {
	afterTransition,
	dispatch,
	getClassProperty,
	stringToBoolean,
} from '../../utils';

import { IOverlay, IOverlayOptions } from './interfaces';
import { TOverlayOptionsAutoCloseEqualityType } from './types';
import { ICollectionItem } from '../../interfaces';
import { IAccessibilityComponent } from '../accessibility-manager/interfaces';
import { BREAKPOINTS } from '../../constants';

import HSBasePlugin from '../base-plugin';
import HSAccessibilityObserver from '../accessibility-manager';

class HSOverlay extends HSBasePlugin<{}> implements IOverlay {
	private accessibilityComponent: IAccessibilityComponent;
	private lastFocusedToggle: HTMLElement | null = null;
	private initiallyOpened: boolean;

	private readonly hiddenClass: string | null;
	private readonly emulateScrollbarSpace: boolean;
	private readonly isClosePrev: boolean;
	private readonly backdropClasses: string | null;
	private readonly backdropParent: string | HTMLElement | Document;
	private readonly backdropExtraClasses: string | null;
	private readonly animationTarget: HTMLElement | null;
	private readonly isScrollInsideViewport: boolean;
	private onScrollInsideViewportClickListener: ((e: MouseEvent) => void) | null;

	private openNextOverlay: boolean;
	private autoHide: ReturnType<typeof setTimeout> | null;
	private toggleButtons: HTMLElement[];
	public toggleMinifierButtons: HTMLElement[];

	static openedItemsQty = 0;

	public initContainer: HTMLElement | null;
	public isCloseWhenClickInside: boolean;
	public isTabAccessibilityLimited: boolean;
	public isLayoutAffect: boolean;
	public hasAutofocus: boolean;
	public hasDynamicZIndex: boolean;
	public hasAbilityToCloseOnBackdropClick: boolean;
	public openedBreakpoint: number | null;
	public autoClose: number | null;
	public autoCloseEqualityType: TOverlayOptionsAutoCloseEqualityType | null;
	public moveOverlayToBody: number | null;
	public isToggleClassesImmediately: boolean;

	private backdrop: HTMLElement | null;
	private initialZIndex = 0;
	static currentZIndex = 0;

	private onElementClickListener:
		| {
				el: HTMLElement;
				fn: () => void;
		  }[]
		| null;
	private onElementMinifierClickListener:
		| {
				el: HTMLElement;
				fn: () => void;
		  }[]
		| null;
	private onOverlayClickListener: (evt: Event) => void;
	private onBackdropClickListener: () => void;

	constructor(el: HTMLElement, options?: IOverlayOptions, events?: {}) {
		super(el, options, events);

		if (!window.$hsOverlayCollection) {
			window.$hsOverlayCollection = [];
		}

		// Collect all data options from toggles
		this.toggleButtons = Array.from(
			document.querySelectorAll(`[data-hs-overlay="#${this.el.id}"]`),
		);
		const toggleDataOptions = this.collectToggleParameters(this.toggleButtons);

		this.toggleMinifierButtons = Array.from(
			document.querySelectorAll(`[data-hs-overlay-minifier="#${this.el.id}"]`),
		);

		const data = el.getAttribute('data-hs-overlay-options');
		const dataOptions: IOverlayOptions = data ? JSON.parse(data) : {};
		const concatOptions = {
			...dataOptions,
			...toggleDataOptions,
			...options,
		};

		this.hiddenClass = concatOptions?.hiddenClass || 'hidden';
		this.emulateScrollbarSpace = concatOptions?.emulateScrollbarSpace || false;
		this.isClosePrev = concatOptions?.isClosePrev ?? true;
		this.backdropClasses =
			concatOptions?.backdropClasses ??
			'hs-overlay-backdrop transition duration fixed inset-0 bg-gray-900/50 dark:bg-neutral-900/80';
		this.backdropParent =
			typeof concatOptions.backdropParent === 'string'
				? (document.querySelector(concatOptions.backdropParent) as HTMLElement)
				: document.body;
		this.backdropExtraClasses = concatOptions?.backdropExtraClasses ?? '';
		this.moveOverlayToBody = concatOptions?.moveOverlayToBody || null;
		this.isToggleClassesImmediately =
			concatOptions?.isToggleClassesImmediately ??
			stringToBoolean(
				getClassProperty(this.el, '--toggle-classes-immediately', 'false') ||
					'false',
			);

		this.openNextOverlay = false;
		this.autoHide = null;
		this.initContainer = this.el?.parentElement || null;
		this.isCloseWhenClickInside = stringToBoolean(
			getClassProperty(this.el, '--close-when-click-inside', 'false') ||
				'false',
		);
		this.isTabAccessibilityLimited = stringToBoolean(
			getClassProperty(this.el, '--tab-accessibility-limited', 'true') ||
				'true',
		);
		this.isLayoutAffect = stringToBoolean(
			getClassProperty(this.el, '--is-layout-affect', 'false') || 'false',
		);
		this.hasAutofocus = stringToBoolean(
			getClassProperty(this.el, '--has-autofocus', 'true') || 'true',
		);
		this.hasDynamicZIndex = stringToBoolean(
			getClassProperty(this.el, '--has-dynamic-z-index', 'false') || 'false',
		);
		this.hasAbilityToCloseOnBackdropClick = stringToBoolean(
			this.el.getAttribute('data-hs-overlay-keyboard') || 'true',
		);
		this.isScrollInsideViewport =
			getClassProperty(this.el, '--overlay-scroll-behavior') ===
			'inside-viewport';
		this.onScrollInsideViewportClickListener = null;

		const autoCloseBreakpoint = getClassProperty(this.el, '--auto-close');
		const autoCloseEqualityType = getClassProperty(
			this.el,
			'--auto-close-equality-type',
		);
		const openedBreakpoint = getClassProperty(this.el, '--opened');

		this.autoClose =
			!isNaN(+autoCloseBreakpoint) && isFinite(+autoCloseBreakpoint)
				? +autoCloseBreakpoint
				: BREAKPOINTS[autoCloseBreakpoint] || null;
		this.autoCloseEqualityType =
			(autoCloseEqualityType as TOverlayOptionsAutoCloseEqualityType) ?? null;
		this.openedBreakpoint =
			(!isNaN(+openedBreakpoint) && isFinite(+openedBreakpoint)
				? +openedBreakpoint
				: BREAKPOINTS[openedBreakpoint]) || null;

		this.animationTarget =
			this?.el?.querySelector('.hs-overlay-animation-target') || this.el;

		this.initialZIndex = parseInt(getComputedStyle(this.el).zIndex, 10);

		this.onElementClickListener = [];
		this.onElementMinifierClickListener = [];

		this.initiallyOpened = this.isOpened();

		this.init();
	}

	private elementClick() {
		const payloadFn = () => {
			const payload = {
				el: this.el,
				isOpened: !!this.el.classList.contains('open'),
			};

			this.fireEvent('toggleClicked', payload);
			dispatch('toggleClicked.hs.overlay', this.el, payload);
		};

		if (this.el.classList.contains('opened')) this.close(false, payloadFn);
		else this.open(payloadFn);
	}

	private elementMinifierClick() {
		const payloadFn = () => {
			const payload = {
				el: this.el,
				isMinified: !!this.el.classList.contains('minified'),
			};

			this.fireEvent('toggleMinifierClicked', payload);
			dispatch('toggleMinifierClicked.hs.overlay', this.el, payload);
		};

		if (this.el.classList.contains('minified')) this.minify(false, payloadFn);
		else this.minify(true, payloadFn);
	}

	public minify(isMinified: boolean, cb: Function | null = null) {
		if (isMinified) {
			this.el.classList.add('minified');
			document.body.classList.add('hs-overlay-minified');

			if (cb) cb();
		} else {
			this.el.classList.remove('minified');
			document.body.classList.remove('hs-overlay-minified');

			if (cb) cb();
		}
	}

	private overlayClick(evt: Event) {
		if (
			(evt.target as HTMLElement).id &&
			(evt.target as HTMLElement).id === this.el.id &&
			// `#${(evt.target as HTMLElement).id}` === this.el.id &&
			this.isCloseWhenClickInside &&
			this.hasAbilityToCloseOnBackdropClick
		) {
			this.close();
		}
	}

	private backdropClick() {
		this.close();
	}

	private init() {
		this.createCollection(window.$hsOverlayCollection, this);

		if (this.isLayoutAffect && this.openedBreakpoint) {
			const instance = HSOverlay.getInstance(this.el, true);

			HSOverlay.setOpened(
				this.openedBreakpoint,
				instance as ICollectionItem<HSOverlay>,
			);
		}

		this.onOverlayClickListener = (evt) => this.overlayClick(evt);

		this.el.addEventListener('click', this.onOverlayClickListener);

		if (this.toggleButtons.length) this.buildToggleButtons(this.toggleButtons);

		if (this.toggleMinifierButtons.length) this.buildToggleMinifierButtons();

		if (typeof window !== 'undefined') {
			if (!window.HSAccessibilityObserver) {
				window.HSAccessibilityObserver = new HSAccessibilityObserver();
			}
			this.setupAccessibility();
		}
	}

	private buildToggleButtons(buttons: HTMLElement[]) {
		buttons.forEach((el) => {
			if (this.el.classList.contains('opened')) el.ariaExpanded = 'true';
			else el.ariaExpanded = 'false';

			this.onElementClickListener.push({
				el,
				fn: () => this.elementClick(),
			});

			el.addEventListener(
				'click',
				this.onElementClickListener.find(
					(toggleButton) => toggleButton.el === el,
				).fn,
			);
		});
	}

	private buildToggleMinifierButtons() {
		this.toggleMinifierButtons.forEach((el) => {
			if (this.el.classList.contains('minified')) el.ariaExpanded = 'true';
			else el.ariaExpanded = 'false';

			this.onElementMinifierClickListener.push({
				el,
				fn: () => this.elementMinifierClick(),
			});

			el.addEventListener(
				'click',
				this.onElementMinifierClickListener.find(
					(minifierButton) => minifierButton.el === el,
				).fn,
			);
		});
	}

	private hideAuto() {
		const time = parseInt(getClassProperty(this.el, '--auto-hide', '0'));

		if (time) {
			this.autoHide = setTimeout(() => {
				this.close();
			}, time);
		}
	}

	private checkTimer() {
		if (this.autoHide) {
			clearTimeout(this.autoHide);

			this.autoHide = null;
		}
	}

	private buildBackdrop() {
		const overlayClasses = this.el.classList.value.split(' ');
		const overlayZIndex = parseInt(
			window.getComputedStyle(this.el).getPropertyValue('z-index'),
		);
		const backdropId =
			this.el.getAttribute('data-hs-overlay-backdrop-container') || false;
		this.backdrop = document.createElement('div');
		let backdropClasses = `${this.backdropClasses} ${this.backdropExtraClasses}`;
		const closeOnBackdrop =
			getClassProperty(this.el, '--overlay-backdrop', 'true') !== 'static';
		const disableBackdrop =
			getClassProperty(this.el, '--overlay-backdrop', 'true') === 'false';

		this.backdrop.id = `${this.el.id}-backdrop`;
		if ('style' in this.backdrop) {
			this.backdrop.style.zIndex = `${overlayZIndex - 1}`;
		}

		for (const value of overlayClasses) {
			if (
				value.startsWith('hs-overlay-backdrop-open:') ||
				value.includes(':hs-overlay-backdrop-open:')
			) {
				backdropClasses += ` ${value}`;
			}
		}

		if (disableBackdrop) return;

		if (backdropId) {
			this.backdrop = document
				.querySelector(backdropId)
				.cloneNode(true) as HTMLElement;
			this.backdrop.classList.remove('hidden');

			backdropClasses = `${(
				this.backdrop as HTMLElement
			).classList.toString()}`;
			this.backdrop.classList.value = '';
		}

		if (closeOnBackdrop) {
			this.onBackdropClickListener = () => this.backdropClick();

			this.backdrop.addEventListener(
				'click',
				this.onBackdropClickListener,
				true,
			);
		}

		this.backdrop.setAttribute('data-hs-overlay-backdrop-template', '');

		(this.backdropParent as HTMLElement).appendChild(this.backdrop);

		setTimeout(() => {
			this.backdrop.classList.value = backdropClasses;
		});
	}

	private destroyBackdrop() {
		const backdrop: HTMLElement = document.querySelector(
			`#${this.el.id}-backdrop`,
		);

		if (!backdrop) return;

		if (this.openNextOverlay) {
			backdrop.style.transitionDuration = `${
				parseFloat(
					window
						.getComputedStyle(backdrop)
						.transitionDuration.replace(/[^\d.-]/g, ''),
				) * 1.8
			}s`;
		}

		backdrop.classList.add('opacity-0');

		afterTransition(backdrop, () => {
			backdrop.remove();
		});
	}

	private focusElement() {
		const input: HTMLInputElement = this.el.querySelector('[autofocus]');

		if (!input) return false;
		else input.focus();
	}

	private getBodyCurrentScrollbarSize(): number {
		return Math.max(
			window.innerWidth - document.documentElement.clientWidth,
			0,
		);
	}

	private collectToggleParameters(buttons: HTMLElement[]) {
		let toggleData = {};

		buttons.forEach((el) => {
			const data = el.getAttribute('data-hs-overlay-options');
			const dataOptions: IOverlayOptions = data ? JSON.parse(data) : {};
			toggleData = {
				...toggleData,
				...dataOptions,
			};
		});

		return toggleData;
	}

	private isElementVisible(): boolean {
		const style = window.getComputedStyle(this.el);

		if (
			style.display === 'none' ||
			style.visibility === 'hidden' ||
			style.opacity === '0'
		) {
			return false;
		}

		const rect = this.el.getBoundingClientRect();

		if (rect.width === 0 || rect.height === 0) {
			return false;
		}

		let parent = this.el.parentElement;

		while (parent) {
			const parentStyle = window.getComputedStyle(parent);

			if (
				parentStyle.display === 'none' ||
				parentStyle.visibility === 'hidden' ||
				parentStyle.opacity === '0'
			) {
				return false;
			}

			parent = parent.parentElement;
		}

		return true;
	}

	private isOpened(): boolean {
		return (
			this.el.classList.contains('open') &&
			!this.el.classList.contains(this.hiddenClass)
		);
	}

	// Public methods
	public open(cb: Function | null = null) {
		if (!window.$hsOverlayCollection) {
			window.$hsOverlayCollection = [];
		}

		if (this.el.classList.contains('minified')) {
			this.minify(false);
		}

		if (this.hasDynamicZIndex) {
			if (HSOverlay.currentZIndex < this.initialZIndex) {
				HSOverlay.currentZIndex = this.initialZIndex;
			}

			HSOverlay.currentZIndex++;
			this.el.style.zIndex = `${HSOverlay.currentZIndex}`;
		}

		const openedOverlays = document.querySelectorAll('.hs-overlay.open');
		const currentlyOpened = window.$hsOverlayCollection.find(
			(el) =>
				Array.from(openedOverlays).includes(el.element.el) &&
				!el.element.isLayoutAffect,
		);
		const toggles = document.querySelectorAll(
			`[data-hs-overlay="#${this.el.id}"]`,
		);
		const disabledScroll =
			getClassProperty(this.el, '--body-scroll', 'false') !== 'true';

		this.lastFocusedToggle = document.activeElement as HTMLElement;

		if (this.isClosePrev && currentlyOpened) {
			this.openNextOverlay = true;

			return currentlyOpened.element.close().then(() => {
				this.open();

				this.openNextOverlay = false;
			});
		}

		if (disabledScroll) {
			if (this.emulateScrollbarSpace) {
				document.body.style.paddingRight = `${this.getBodyCurrentScrollbarSize()}px`;
			}

			document.body.style.overflow = 'hidden';
		}

		this.buildBackdrop();
		this.checkTimer();
		this.hideAuto();

		toggles.forEach((toggle) => {
			if (toggle.ariaExpanded) toggle.ariaExpanded = 'true';
		});
		this.el.classList.remove(this.hiddenClass);
		this.el.setAttribute('aria-overlay', 'true');
		this.el.setAttribute('tabindex', '-1');

		const openFn = () => {
			if (this.el.classList.contains('opened')) return false;

			this.el.classList.add('open', 'opened');
			if (this.isLayoutAffect) {
				document.body.classList.add('hs-overlay-body-open');
			}

			if (!this.initiallyOpened && this.hasAutofocus) {
				this.el.focus({ preventScroll: true });

				this.el.style.outline = 'none';
			}
			this.initiallyOpened = false;

			this.fireEvent('open', this.el);
			dispatch('open.hs.overlay', this.el, this.el);

			if (window.HSAccessibilityObserver && this.accessibilityComponent) {
				window.HSAccessibilityObserver.updateComponentState(
					this.accessibilityComponent,
					true,
				);
			}

			if (this.hasAutofocus) this.focusElement();

			if (typeof cb === 'function') cb();

			if (this.isScrollInsideViewport) {
				this.el.style.pointerEvents = 'auto';
				this.onScrollInsideViewportClickListener = (e: MouseEvent) => {
					if (e.target === this.el && this.hasAbilityToCloseOnBackdropClick) {
						this.close();
					}
				};
				this.el.addEventListener(
					'click',
					this.onScrollInsideViewportClickListener,
				);
			}

			if (this.isElementVisible()) HSOverlay.openedItemsQty++;
		};

		if (this.isToggleClassesImmediately) openFn();
		else setTimeout(openFn, 50);
	}

	public close(forceClose = false, cb: Function | null = null) {
		if (!window.$hsOverlayCollection) {
			window.$hsOverlayCollection = [];
		}

		if (this.isElementVisible()) {
			HSOverlay.openedItemsQty =
				HSOverlay.openedItemsQty <= 0 ? 0 : HSOverlay.openedItemsQty - 1;
		}

		if (HSOverlay.openedItemsQty === 0 && this.isLayoutAffect) {
			document.body.classList.remove('hs-overlay-body-open');
		}

		const closeFn = (_cb: Function) => {
			if (this.el.classList.contains('open')) return false;
			const toggles = document.querySelectorAll(
				`[data-hs-overlay="#${this.el.id}"]`,
			);

			toggles.forEach((toggle) => {
				if (toggle.ariaExpanded) toggle.ariaExpanded = 'false';
			});
			this.el.classList.add(this.hiddenClass);
			if (this.hasDynamicZIndex) this.el.style.zIndex = '';

			this.destroyBackdrop();

			this.fireEvent('close', this.el);
			dispatch('close.hs.overlay', this.el, this.el);

			if (window.HSAccessibilityObserver && this.accessibilityComponent) {
				window.HSAccessibilityObserver.updateComponentState(
					this.accessibilityComponent,
					false,
				);
			}

			const hasOpenedOverlays = window.$hsOverlayCollection.some(
				(overlay) =>
					overlay.element.el.classList.contains('opened') &&
					!overlay.element.isLayoutAffect,
			);

			if (!hasOpenedOverlays) {
				document.body.style.overflow = '';
				if (this.emulateScrollbarSpace) document.body.style.paddingRight = '';
			}

			if (this.lastFocusedToggle) {
				this.lastFocusedToggle.focus();
				this.lastFocusedToggle = null;
			}

			_cb(this.el);
			if (typeof cb === 'function') cb();

			if (HSOverlay.openedItemsQty === 0) {
				if (!this.isLayoutAffect) {
					const hasLayoutAffectOverlays = window.$hsOverlayCollection.some(
						(overlay) =>
							overlay.element.isLayoutAffect &&
							overlay.element.el.classList.contains('opened'),
					);

					if (!hasLayoutAffectOverlays) {
						document.body.classList.remove('hs-overlay-body-open');
					}
				}

				if (this.hasDynamicZIndex) HSOverlay.currentZIndex = 0;
			}
		};

		return new Promise((resolve) => {
			this.el.classList.remove('open', 'opened');
			this.el.removeAttribute('aria-overlay');
			this.el.removeAttribute('tabindex');

			this.el.style.outline = '';

			if (this.isScrollInsideViewport) {
				this.el.style.pointerEvents = '';

				if (this.onScrollInsideViewportClickListener) {
					this.el.removeEventListener(
						'click',
						this.onScrollInsideViewportClickListener,
					);

					this.onScrollInsideViewportClickListener = null;
				}
			}

			if (forceClose || this.isToggleClassesImmediately) closeFn(resolve);
			else afterTransition(this.animationTarget, () => closeFn(resolve));
		});
	}

	public updateToggles() {
		const found = Array.from(
			document.querySelectorAll<HTMLElement>(
				`[data-hs-overlay="#${this.el.id}"]`,
			),
		);
		const newButtons = found.filter((btn) => !this.toggleButtons.includes(btn));

		if (newButtons.length) {
			this.toggleButtons.push(...newButtons);
			this.buildToggleButtons(newButtons);
		}

		this.toggleButtons = this.toggleButtons.filter((btn) => {
			if (document.contains(btn)) return true;

			const listener = this.onElementClickListener?.find(
				(lst) => lst.el === btn,
			);
			if (listener) btn.removeEventListener('click', listener.fn as () => void);

			return false;
		});
	}

	public destroy() {
		if (!window.$hsOverlayCollection) return;

		// Remove classes
		this.el.classList.remove('open', 'opened', this.hiddenClass);
		if (this.isLayoutAffect) {
			document.body.classList.remove('hs-overlay-body-open');
		}

		// Remove listeners
		this.el.removeEventListener('click', this.onOverlayClickListener);
		if (this.onElementClickListener.length) {
			this.onElementClickListener.forEach(({ el, fn }) => {
				el.removeEventListener('click', fn);
			});
			this.onElementClickListener = null;
		}
		if (this.backdrop) {
			this.backdrop.removeEventListener('click', this.onBackdropClickListener);
		}

		if (this.backdrop) {
			this.backdrop.remove();
			this.backdrop = null;
		}

		if (this.onScrollInsideViewportClickListener) {
			this.el.removeEventListener(
				'click',
				this.onScrollInsideViewportClickListener,
			);

			this.onScrollInsideViewportClickListener = null;
		}

		window.$hsOverlayCollection = window.$hsOverlayCollection.filter(
			({ element }) => element.el !== this.el,
		);
	}

	// Static methods
	private static findInCollection(
		target: HSOverlay | HTMLElement | string,
	): ICollectionItem<HSOverlay> | null {
		if (!window.$hsOverlayCollection) return null;

		return (
			window.$hsOverlayCollection.find((el) => {
				if (target instanceof HSOverlay) return el.element.el === target.el;
				else if (typeof target === 'string') {
					return el.element.el === document.querySelector(target);
				} else return el.element.el === target;
			}) || null
		);
	}

	static getInstance(target: HTMLElement | string, isInstance?: boolean) {
		// Backward compatibility
		const _temp =
			typeof target === 'string' ? document.querySelector(target) : target;
		const _target = _temp?.getAttribute('data-hs-overlay')
			? _temp.getAttribute('data-hs-overlay')
			: target;

		const elInCollection = window.$hsOverlayCollection.find(
			(el) =>
				el.element.el ===
					(typeof _target === 'string'
						? document.querySelector(_target)
						: _target) ||
				el.element.el ===
					(typeof _target === 'string'
						? document.querySelector(_target)
						: _target),
		);

		return elInCollection
			? isInstance
				? elInCollection
				: elInCollection.element.el
			: null;
	}

	static autoInit() {
		if (!window.$hsOverlayCollection) {
			window.$hsOverlayCollection = [];
		}

		if (window.$hsOverlayCollection) {
			window.$hsOverlayCollection = window.$hsOverlayCollection.filter(
				({ element }) => document.contains(element.el),
			);
		}

		HSOverlay.openedItemsQty = window.$hsOverlayCollection.filter(
			({ element }) => element.el.classList.contains('opened'),
		).length;

		document
			.querySelectorAll('.hs-overlay:not(.--prevent-on-load-init)')
			.forEach((el: HTMLElement) => {
				if (
					!window.$hsOverlayCollection.find(
						(elC) => (elC?.element?.el as HTMLElement) === el,
					)
				) {
					new HSOverlay(el);
				}
			});

		window.$hsOverlayCollection.forEach(({ element }) => {
			element.updateToggles();
		});
	}

	static open(target: HSOverlay | HTMLElement | string) {
		const instance = HSOverlay.findInCollection(target);

		if (
			instance &&
			instance.element.el.classList.contains(instance.element.hiddenClass)
		)
			instance.element.open();
	}

	static close(target: HSOverlay | HTMLElement | string) {
		const instance = HSOverlay.findInCollection(target);

		if (
			instance &&
			!instance.element.el.classList.contains(instance.element.hiddenClass)
		)
			instance.element.close();
	}

	static minify(target: HSOverlay | HTMLElement | string, isMinified: boolean) {
		const instance = HSOverlay.findInCollection(target);

		if (instance) {
			instance.element.minify(isMinified);
		}
	}

	static setOpened(breakpoint: number, el: ICollectionItem<HSOverlay>) {
		if (document.body.clientWidth >= breakpoint) {
			if (el.element.el.classList.contains('minified')) {
				el.element.minify(false);
			}

			document.body.classList.add('hs-overlay-body-open');

			el.element.open();
		} else el.element.close(true);
	}

	// Accessibility methods
	private setupAccessibility(): void {
		this.accessibilityComponent =
			window.HSAccessibilityObserver.registerComponent(
				this.el,
				{
					onEnter: () => {
						if (!this.isOpened()) this.open();
					},
					onEsc: () => {
						if (this.isOpened() && this.hasAbilityToCloseOnBackdropClick) {
							this.close();
						}
					},
					onTab: () => {
						if (!this.isOpened() || !this.isTabAccessibilityLimited) return;

						const focusableElements = Array.from(
							this.el.querySelectorAll<HTMLElement>(
								'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
							),
						).filter(
							(el) =>
								!el.hidden && window.getComputedStyle(el).display !== 'none',
						);

						if (focusableElements.length === 0) return;

						const focusedElement = this.el.querySelector(':focus');
						const currentIndex = focusedElement
							? focusableElements.indexOf(focusedElement as HTMLElement)
							: -1;
						const isShiftPressed =
							window.event instanceof KeyboardEvent && window.event.shiftKey;

						if (isShiftPressed) {
							if (currentIndex <= 0) {
								focusableElements[focusableElements.length - 1].focus();
							} else {
								focusableElements[currentIndex - 1].focus();
							}
						} else {
							if (currentIndex === focusableElements.length - 1) {
								focusableElements[0].focus();
							} else {
								focusableElements[currentIndex + 1].focus();
							}
						}

						window.event?.preventDefault();
					},
				},
				this.isOpened(),
				'Overlay',
				'.hs-overlay',
			);

		this.toggleButtons.forEach((toggleButton) => {
			window.HSAccessibilityObserver.registerComponent(
				toggleButton,
				{
					onEnter: () => {
						if (!this.isOpened()) this.open();
						else this.close();
					},
					onEsc: () => {
						if (this.isOpened() && this.hasAbilityToCloseOnBackdropClick) {
							this.close();
						}
					},
				},
				this.isOpened(),
				'Overlay Toggle',
				`[data-hs-overlay="#${this.el.id}"]`,
			);
		});
	}

	// Backward compatibility
	static on(
		evt: string,
		target: HSOverlay | HTMLElement | string,
		cb: Function,
	) {
		const instance = HSOverlay.findInCollection(target);

		if (instance) instance.element.events[evt] = cb;
	}
}

export default HSOverlay;
