import {
	Instance as PopperInstance,
	createPopper,
	Placement,
	VirtualElement,
} from '@popperjs/core';
import KTDom from '../../helpers/dom';
import KTUtils from '../../helpers/utils';
import KTData from '../../helpers/data';
import KTEventHandler from '../../helpers/event-handler';
import KTComponent from '../component';
import { KTMenuConfigInterface, KTMenuInterface } from './types';
import { KTMenuItemToggleType, KTMenuItemTriggerType } from './types';
import { KTOptionType } from '../../types';
import { KT_ACCESSIBILITY_KEYS } from '../constants';
declare global {
	interface Window {
		KT_MENU_INITIALIZED: boolean;
	}
}
import { KTDropdown } from '@keenthemes/ktui/src/components/dropdown';

export class KTMenu extends KTComponent implements KTMenuInterface {
	protected override _name: string = 'menu';
	protected override _defaultConfig: KTMenuConfigInterface = {
		dropdownZindex: '105',
		dropdownHoverTimeout: 200,
		dropdownPlacement: 'bottom',
		dropdownOffset: '0, 5px',
		accordionExpandAll: false,
		preserveParentDropdowns: true,
	};
	protected override _config: KTMenuConfigInterface = this._defaultConfig;
	protected _disabled: boolean = false;

	constructor(element: HTMLElement, config?: KTMenuConfigInterface) {
		super();

		if (KTData.has(element as HTMLElement, this._name)) return;

		this._init(element);
		this._buildConfig(config);
		this._update();
	}

	protected _click(element: HTMLElement, event: Event): void {
		if (element.hasAttribute('href') && element.getAttribute('href') !== '#') {
			return;
		}

		event.preventDefault();
		event.stopPropagation();

		if (this._disabled === true) {
			return;
		}

		const itemElement = this._getItemElement(element);
		if (!itemElement) return;

		if (this._getItemOption(itemElement, 'trigger') !== 'click') {
			return;
		}

		if (this._getItemOption(itemElement, 'toggle') === false) {
			this._show(itemElement);
		} else {
			this._toggle(itemElement);
		}
	}

	protected _link(element: HTMLElement, event: Event): void {
		if (this._disabled === true) {
			return;
		}

		let payload: {
			cancel?: boolean;
			element: HTMLElement;
			event: Event;
		} = {
			cancel: false,
			element,
			event,
		};
		this._fireEvent('link.click', payload);
		this._dispatchEvent('link.click', payload);
		if (payload.cancel === true) {
			return;
		}

		const itemElement = this._getItemElement(element);
		if (this._isItemDropdownPermanent(itemElement) === false) {
			KTMenu.hide();
		}

		payload = {
			element,
			event,
		};
		this._fireEvent('link.clicked', payload);
		this._dispatchEvent('link.clicked', payload);
	}

	protected _dismiss(element: HTMLElement): void {
		const itemElement = this._getItemElement(element);
		if (!itemElement) return;
		const itemElements = this._getItemChildElements(itemElement);

		if (
			itemElement !== null &&
			this._getItemToggleMode(itemElement) === 'dropdown'
		) {
			// hide items dropdown
			this._hide(itemElement);

			// Hide all child elements as well
			itemElements.forEach((each) => {
				if (this._getItemToggleMode(each as HTMLElement) === 'dropdown') {
					this._hide(each);
				}
			});
		}
	}

	protected _mouseover(element: HTMLElement): void {
		const itemElement = this._getItemElement(element);
		if (!itemElement) return;

		if (this._disabled === true) {
			return;
		}

		if (itemElement === null) {
			return;
		}

		if (this._getItemOption(itemElement, 'trigger') !== 'hover') {
			return;
		}

		if (KTData.get(itemElement, 'hover') === '1') {
			clearTimeout(KTData.get(itemElement, 'timeout') as number);
			KTData.remove(itemElement, 'hover');
			KTData.remove(itemElement, 'timeout');
		}

		this._show(itemElement);
	}

	protected _mouseout(element: HTMLElement): void {
		const itemElement = this._getItemElement(element);
		if (!itemElement) return;

		if (this._disabled === true) {
			return;
		}

		if (this._getItemOption(itemElement, 'trigger') !== 'hover') {
			return;
		}

		// Check if we're moving to a child dropdown
		const relatedTarget = (event as MouseEvent).relatedTarget as HTMLElement;
		if (relatedTarget) {
			const childItemElement = this._getItemElement(relatedTarget);
			if (childItemElement && this._isChildOfParent(childItemElement, itemElement)) {
				// Don't hide parent when moving to child
				return;
			}
		}

		const timeout = setTimeout(() => {
			if (KTData.get(itemElement, 'hover') === '1') {
				this._hide(itemElement);
			}
		}, parseInt(this._getOption('dropdownHoverTimeout') as string));

		KTData.set(itemElement, 'hover', '1');
		KTData.set(itemElement, 'timeout', timeout);
	}

	protected _toggle(itemElement: HTMLElement): void {
		if (this._isItemSubShown(itemElement) === true) {
			this._hide(itemElement);
		} else {
			this._show(itemElement);
		}
	}

	protected _show(itemElement: HTMLElement): void {
		if (this._isItemSubShown(itemElement) === true) {
			return;
		}

		if (this._getItemToggleMode(itemElement) === 'dropdown') {
			this._showDropdown(itemElement);
		} else if (this._getItemToggleMode(itemElement) === 'accordion') {
			this._showAccordion(itemElement);
		}

		// Remember last submenu type
		KTData.set(itemElement, 'toggle', this._getItemToggleMode(itemElement));
	}

	protected _hide(itemElement: HTMLElement): void {
		if (this._isItemSubShown(itemElement) === false) {
			return;
		}

		if (this._getItemToggleMode(itemElement) === 'dropdown') {
			this._hideDropdown(itemElement);
		} else if (this._getItemToggleMode(itemElement) === 'accordion') {
			this._hideAccordion(itemElement);
		}
	}

	protected _reset(itemElement: HTMLElement): void {
		if (this._hasItemSub(itemElement) === false) {
			return;
		}

		const subElement = this._getItemSubElement(itemElement);

		// Reset sub state if sub type is changed during the window resize
		if (
			KTData.has(itemElement, 'toggle') &&
			KTData.get(itemElement, 'toggle') !== this._getItemToggleMode(itemElement)
		) {
			itemElement.classList.remove('show');
			subElement?.classList.remove('show');
		}
	}

	protected _update(): void {
		if (!this._element) return;
		const itemElements = this._element.querySelectorAll(
			'[data-kt-menu-item-trigger]'
		);

		itemElements.forEach((itemElement) => {
			this._updateItemSubType(itemElement as HTMLElement);
			this._reset(itemElement as HTMLElement);
		});
	}

	protected _updateItemSubType(itemElement: HTMLElement): void {
		const subElement = this._getItemSubElement(itemElement as HTMLElement);

		if (subElement) {
			if (this._getItemToggleMode(itemElement as HTMLElement) === 'dropdown') {
				itemElement.classList.remove('kt-menu-item-accordion');
				itemElement.classList.add('kt-menu-item-dropdown');

				subElement.classList.remove('kt-menu-accordion');
				subElement.classList.add('kt-menu-dropdown');
			} else {
				itemElement.classList.remove('kt-menu-item-dropdown');
				itemElement.classList.add('kt-menu-item-accordion');

				subElement.classList.remove('kt-menu-dropdown');
				subElement.classList.add('kt-menu-accordion');
			}
		}
	}

	protected _isItemSubShown(itemElement: HTMLElement): boolean {
		const subElement = this._getItemSubElement(itemElement);

		if (subElement !== null) {
			if (this._getItemToggleMode(itemElement) === 'dropdown') {
				if (
					subElement.classList.contains('show') === true &&
					subElement.hasAttribute('data-popper-placement') === true
				) {
					return true;
				} else {
					return false;
				}
			} else {
				return itemElement.classList.contains('show');
			}
		} else {
			return false;
		}
	}

	protected _isItemDropdownPermanent(itemElement: HTMLElement): boolean {
		return this._getItemOption(itemElement, 'permanent') as boolean;
	}

	protected _isItemParentShown(itemElement: HTMLElement): boolean {
		const parents = KTDom.parents(itemElement, '.kt-menu-item.show');

		return parents && parents.length > 0 ? true : false;
	}

	protected _isItemSubElement(itemElement: HTMLElement): boolean {
		return (
			itemElement.classList.contains('kt-menu-dropdown') ||
			itemElement.classList.contains('kt-menu-accordion')
		);
	}

	protected _hasItemSub(itemElement: HTMLElement): boolean {
		return (
			itemElement.classList.contains('kt-menu-item') &&
			itemElement.hasAttribute('data-kt-menu-item-trigger')
		);
	}

	protected _getItemLinkElement(itemElement: HTMLElement): HTMLElement {
		return KTDom.child(itemElement, '.kt-menu-link, .kt-menu-toggle');
	}

	protected _getItemSubElement(itemElement: HTMLElement): HTMLElement {
		if (
			itemElement.classList.contains('kt-menu-dropdown') === true ||
			itemElement.classList.contains('kt-menu-accordion') === true
		) {
			return itemElement;
		} else if (KTData.has(itemElement, 'sub')) {
			return KTData.get(itemElement, 'sub') as HTMLElement;
		} else {
			return KTDom.child(itemElement, '.kt-menu-dropdown, .kt-menu-accordion');
		}
	}

	protected _getItemToggleMode(itemElement: HTMLElement): KTMenuItemToggleType {
		const itemEl = this._getItemElement(itemElement);

		if (this._getItemOption(itemEl, 'toggle') === 'dropdown') {
			return 'dropdown';
		} else {
			return 'accordion';
		}
	}

	protected _getItemElement(element: HTMLElement): HTMLElement {
		if (
			element.classList.contains('kt-menu-item') &&
			element.hasAttribute('data-kt-menu-item-toggle')
		) {
			return element;
		}

		// Element has item DOM reference in it's data storage
		if (KTData.has(element, 'item')) {
			return KTData.get(element, 'item') as HTMLElement;
		}

		// Item is parent of element
		const itemElement = element.closest(
			'.kt-menu-item[data-kt-menu-item-toggle]'
		);
		if (itemElement) {
			return itemElement as HTMLElement;
		}

		// Element's parent has item DOM reference in it's data storage
		const subElement = element.closest(
			'.kt-menu-dropdown, .kt-menu-accordion'
		) as HTMLElement;
		if (subElement) {
			if (KTData.has(subElement, 'item') === true) {
				return KTData.get(subElement, 'item') as HTMLElement;
			}
		}

		return null;
	}

	protected _getItemParentElement(itemElement: HTMLElement): HTMLElement {
		const subElement: HTMLElement = itemElement.closest(
			'.kt-menu-dropdown, .kt-menu-accordion'
		);
		let parentItem: HTMLElement;

		if (subElement && KTData.has(subElement, 'item')) {
			return KTData.get(subElement, 'item') as HTMLElement;
		}

		if (
			subElement &&
			(parentItem = subElement.closest(
				'.kt-menu-item[data-kt-menu-item-trigger]'
			))
		) {
			return parentItem;
		}

		return null;
	}

	protected _getItemParentElements(
		itemElement: HTMLElement
	): Array<HTMLElement> {
		const parentElements: Array<HTMLElement> = [];
		let parentElement: HTMLElement;
		let i = 0;

		do {
			parentElement = this._getItemParentElement(itemElement);

			if (parentElement) {
				parentElements.push(parentElement);
				itemElement = parentElement;
			}

			i++;
		} while (parent !== null && i < 20);

		return parentElements;
	}

	protected _getItemChildElement(itemElement: HTMLElement): HTMLElement {
		let selector: HTMLElement = itemElement;
		let element: HTMLElement;

		if (KTData.has(itemElement, 'sub')) {
			selector = KTData.get(itemElement, 'sub') as HTMLElement;
		}

		if (selector !== null) {
			//element = selector.querySelector('.show.kt-menu-item[data-kt-menu-trigger]');
			element = selector.querySelector(
				'.kt-menu-item[data-kt-menu-item-trigger]'
			);

			if (element) {
				return element;
			} else {
				return null;
			}
		} else {
			return null;
		}
	}

	protected _getItemChildElements(
		itemElement: HTMLElement
	): Array<HTMLElement> {
		const children: Array<HTMLElement> = [];
		let child: HTMLElement;
		let i = 0;
		let buffer = itemElement;
		do {
			child = this._getItemChildElement(buffer);

			if (child) {
				children.push(child);
				buffer = child as HTMLElement;
			}

			i++;
		} while (child !== null && i < 20);

		return children;
	}

	protected _isChildOfParent(
		childElement: HTMLElement,
		parentElement: HTMLElement
	): boolean {
		// Guard clause: validate inputs
		if (!childElement || !parentElement) {
			return false;
		}

		// Guard clause: elements must be in the DOM
		if (!document.contains(childElement) || !document.contains(parentElement)) {
			return false;
		}

		// Check if child is a direct child of parent
		const childSubElement = this._getItemSubElement(childElement);
		if (childSubElement && parentElement.contains(childSubElement)) {
			return true;
		}

		// Check if child is within parent's dropdown
		const parentSubElement = this._getItemSubElement(parentElement);
		if (parentSubElement && parentSubElement.contains(childElement)) {
			return true;
		}

		// Check DOM hierarchy
		return parentElement.contains(childElement);
	}

	protected _hideUnrelatedDropdowns(currentItemElement: HTMLElement): void {
		// Guard clause: validate input
		if (!currentItemElement) {
			return;
		}

		// Check if parent dropdown preservation is enabled
		const preserveParents = this._getOption('preserveParentDropdowns') as boolean;

		const itemElements = document.querySelectorAll(
			'.show.kt-menu-item-dropdown[data-kt-menu-item-trigger]'
		);

		itemElements.forEach((itemElement) => {
			// Guard clause: validate element
			if (!itemElement) {
				return;
			}

			const menu = KTMenu.getInstance(itemElement as HTMLElement);

			if (
				menu &&
				menu.getItemToggleMode(itemElement as HTMLElement) === 'dropdown'
			) {
				// Don't hide the current element
				if (itemElement === currentItemElement) {
					return;
				}

				// Don't hide parent elements of the current element if preservation is enabled
				if (preserveParents && this._isChildOfParent(currentItemElement, itemElement as HTMLElement)) {
					return;
				}

				// Don't hide if current element is a child of this element
				if (this._isChildOfParent(itemElement as HTMLElement, currentItemElement)) {
					return;
				}

				// Hide unrelated dropdowns
				menu.hide(itemElement as HTMLElement);
			}
		});
	}

	protected _showDropdown(itemElement: HTMLElement): void {
		const payload = { cancel: false };
		this._fireEvent('dropdown.show', payload);
		this._dispatchEvent('dropdown.show', payload);
		if (payload.cancel === true) {
			return;
		}

		// Hide all currently shown dropdowns except current one and its parents
		this._hideUnrelatedDropdowns(itemElement);
		if (KTDropdown) {
			KTDropdown.hide(itemElement);
		}

		const subElement: HTMLElement = this._getItemSubElement(itemElement);
		if (!subElement) return;

		const width = this._getItemOption(itemElement, 'width') as string;
		const height = this._getItemOption(itemElement, 'height') as string;

		// Set z=index
		let zindex: number = parseInt(this._getOption('dropdownZindex') as string);
		if (parseInt(KTDom.getCssProp(subElement, 'z-index')) > zindex) {
			zindex = parseInt(KTDom.getCssProp(subElement, 'z-index'));
		}
		if (KTDom.getHighestZindex(itemElement) > zindex) {
			zindex = KTDom.getHighestZindex(itemElement) + 1;
		}
		subElement.style.zIndex = String(zindex);
		// end

		if (width) {
			subElement.style.width = width;
		}

		if (height) {
			subElement.style.height = height;
		}

		subElement.style.display = '';
		subElement.style.overflow = '';

		// Init popper(new)
		this._initDropdownPopper(itemElement, subElement);

		itemElement.classList.add('show');
		itemElement.classList.add('kt-menu-item-dropdown');
		subElement.classList.add('show');

		// Append the sub the the root of the menu
		if (this._getItemOption(itemElement, 'overflow') === true) {
			document.body.appendChild(subElement);
			subElement.setAttribute('data-kt-menu-sub-overflow', 'true');
			KTData.set(itemElement, 'sub', subElement);
			KTData.set(subElement, 'item', itemElement);
			KTData.set(subElement, 'menu', this);
		} else {
			KTData.set(subElement, 'item', itemElement);
		}

		// Handle dropdown shown event
		this._fireEvent('dropdown.shown');
		this._dispatchEvent('dropdown.shown');
	}

	protected _hideDropdown(itemElement: HTMLElement): void {
		const payload = { cancel: false };
		this._fireEvent('dropdown.hide', payload);
		this._dispatchEvent('dropdown.hide', payload);
		if (payload.cancel === true) {
			return;
		}

		const subElement = this._getItemSubElement(itemElement);
		if (!subElement) return;

		subElement.style.zIndex = '';
		subElement.style.width = '';
		subElement.style.height = '';

		itemElement.classList.remove('show');
		itemElement.classList.remove('kt-menu-item-dropdown');
		subElement.classList.remove('show');

		// Append the sub back to it's parent
		if (this._getItemOption(itemElement, 'overflow') === true) {
			subElement.removeAttribute('data-kt-menu-sub-overflow');
			if (itemElement.classList.contains('kt-menu-item')) {
				itemElement.appendChild(subElement);
			} else {
				if (!this._element) return;
				KTDom.insertAfter(this._element, itemElement);
			}

			KTData.remove(itemElement, 'sub');
			KTData.remove(subElement, 'item');
			KTData.remove(subElement, 'menu');
		}

		// Destroy popper(new)
		this._destroyDropdownPopper(itemElement);

		// Handle dropdown hidden event
		this._fireEvent('dropdown.hidden');
		this._dispatchEvent('dropdown.hidden');
	}

	protected _initDropdownPopper(
		itemElement: HTMLElement,
		subElement: HTMLElement
	): void {
		// Setup popper instance
		let reference: HTMLElement;
		const attach: string = this._getItemOption(itemElement, 'attach') as string;

		if (attach) {
			if (attach === 'parent') {
				reference = itemElement.parentNode as HTMLElement;
			} else {
				reference = document.querySelector(attach) as HTMLElement;
			}
		} else {
			reference = itemElement;
		}

		if (reference) {
			const popper = createPopper(
				reference as Element | VirtualElement,
				subElement,
				this._getDropdownPopperConfig(itemElement)
			);
			KTData.set(itemElement, 'popper', popper);
		}
	}

	protected _destroyDropdownPopper(itemElement: HTMLElement): void {
		if (KTData.has(itemElement, 'popper')) {
			(KTData.get(itemElement, 'popper') as PopperInstance).destroy();
			KTData.remove(itemElement, 'popper');
		}
	}

	protected _getDropdownPopperConfig(itemElement: HTMLElement): object {
		const isRtl = KTDom.isRTL();

		// Placement
		let placement = this._getOption('dropdownPlacement') as Placement;
		if (this._getItemOption(itemElement, 'placement')) {
			placement = this._getItemOption(itemElement, 'placement') as Placement;
		}
		if (isRtl && this._getItemOption(itemElement, 'placementRtl')) {
			placement = this._getItemOption(itemElement, 'placementRtl') as Placement;
		}

		// Offset
		let offsetValue = this._getOption('dropdownOffset');
		if (this._getItemOption(itemElement, 'offset')) {
			offsetValue = this._getItemOption(itemElement, 'offset') as Placement;
		}
		if (isRtl && this._getItemOption(itemElement, 'offsetRtl')) {
			offsetValue = this._getItemOption(itemElement, 'offsetRtl') as Placement;
		}
		const offset = offsetValue
			? offsetValue
					.toString()
					.split(',')
					.map((value) => parseInt(value.trim(), 10))
			: [0, 0];

		// Strategy
		const strategy =
			this._getItemOption(itemElement, 'overflow') === true
				? 'absolute'
				: 'fixed';
		const altAxis =
			this._getItemOption(itemElement, 'flip') !== false ? true : false;
		const popperConfig = {
			placement: placement,
			strategy: strategy,
			modifiers: [
				{
					name: 'offset',
					options: {
						offset: offset,
					},
				},
				{
					name: 'preventOverflow',
					options: {
						altAxis: altAxis,
					},
				},
				{
					name: 'flip',
					options: {
						flipVariations: false,
					},
				},
			],
		};

		return popperConfig;
	}

	protected _showAccordion(itemElement: HTMLElement): void {
		const payload = { cancel: false };
		this._fireEvent('accordion.show', payload);
		this._dispatchEvent('accordion.show', payload);
		if (payload.cancel === true) {
			return;
		}

		const subElement = this._getItemSubElement(itemElement);
		if (!subElement) return;
		let expandAll = this._getOption('accordionExpandAll') as boolean;

		if (this._getItemOption(itemElement, 'expandAll') === true) {
			expandAll = true;
		} else if (this._getItemOption(itemElement, 'expandAll') === false) {
			expandAll = false;
		} else if (
			this._element &&
			this._getItemOption(this._element, 'expandAll') === true
		) {
			expandAll = true;
		}

		if (expandAll === false) {
			this._hideAccordions(itemElement);
		}

		if (KTData.has(itemElement, 'popper') === true) {
			this._hideDropdown(itemElement);
		}

		itemElement.classList.add('transitioning');

		subElement.style.height = '0px';
		KTDom.reflow(subElement);
		subElement.style.display = 'flex';
		subElement.style.overflow = 'hidden';
		subElement.style.height = `${subElement.scrollHeight}px`;
		itemElement.classList.add('show');

		KTDom.transitionEnd(subElement, () => {
			itemElement.classList.remove('transitioning');
			subElement.classList.add('show');
			subElement.style.height = '';
			subElement.style.display = '';
			subElement.style.overflow = '';

			// Handle accordion hidden event
			this._fireEvent('accordion.shown', payload);
			this._dispatchEvent('accordion.shown', payload);
		});
	}

	protected _hideAccordion(itemElement: HTMLElement): void {
		const payload = { cancel: false };
		this._fireEvent('accordion.hide', payload);
		this._dispatchEvent('accordion.hide', payload);
		if (payload.cancel === true) {
			return;
		}

		const subElement = this._getItemSubElement(itemElement);
		if (!subElement) return;

		itemElement.classList.add('transitioning');
		itemElement.classList.remove('show');
		subElement.style.height = `${subElement.scrollHeight}px`;
		KTDom.reflow(subElement);
		subElement.style.height = '0px';
		subElement.style.overflow = 'hidden';

		KTDom.transitionEnd(subElement, () => {
			subElement.style.overflow = '';
			itemElement.classList.remove('transitioning');
			subElement.classList.remove('show');

			// Handle accordion hidden event
			this._fireEvent('accordion.hidden');
			this._dispatchEvent('accordion.hidden');
		});
	}

	protected _setActiveLink(linkElement: HTMLElement): void {
		const itemElement = this._getItemElement(linkElement);
		if (!itemElement) return;

		if (!this._element) return;
		const parentItems = this._getItemParentElements(itemElement);
		const activeLinks = this._element.querySelectorAll('.kt-menu-link.active');
		const activeParentItems = this._element.querySelectorAll(
			'.kt-menu-item.here, .kt-menu-item.show'
		);

		if (this._getItemToggleMode(itemElement) === 'accordion') {
			this._showAccordion(itemElement);
		} else {
			itemElement.classList.add('here');
		}

		parentItems?.forEach((parentItem) => {
			if (this._getItemToggleMode(parentItem) === 'accordion') {
				this._showAccordion(parentItem);
			} else {
				parentItem.classList.add('here');
			}
		});

		activeLinks?.forEach((activeLink) => {
			activeLink.classList.remove('active');
		});

		activeParentItems?.forEach((activeParentItem) => {
			if (activeParentItem.contains(itemElement) === false) {
				activeParentItem.classList.remove('here');
				activeParentItem.classList.remove('show');
			}
		});

		linkElement.classList.add('active');
	}

	protected _getLinkByAttribute(
		value: string,
		name: string = 'href'
	): HTMLElement {
		if (!this._element) return null;
		const linkElement = this._element.querySelector(
			`'.kt-menu-link[${name}="${value}"]`
		) as HTMLElement;

		return linkElement && null;
	}

	protected _hideAccordions(itemElement: HTMLElement): void {
		if (!this._element) return;
		const itemsToHide = this._element.querySelectorAll(
			'.show[data-kt-menu-item-trigger]'
		);

		itemsToHide.forEach((itemToHide) => {
			if (
				this._getItemToggleMode(itemToHide as HTMLElement) === 'accordion' &&
				(itemToHide as HTMLElement) !== itemElement &&
				itemElement?.contains(itemToHide as HTMLElement) === false &&
				itemToHide.contains(itemElement) === false
			) {
				this._hideAccordion(itemToHide as HTMLElement);
			}
		});
	}

	protected _getItemOption(element: HTMLElement, name: string): KTOptionType {
		let attr: string | null;
		let value = null;
		name = KTUtils.camelReverseCase(name);

		if (element && element.hasAttribute(`data-kt-menu-item-${name}`)) {
			attr = element.getAttribute(`data-kt-menu-item-${name}`);
			if (!attr) return null;

			value = this._getResponsiveOption(attr);
		}

		return value;
	}

	// General Methods
	public getItemTriggerMode(itemElement: HTMLElement): KTMenuItemTriggerType {
		return this._getItemOption(itemElement, 'trigger') as KTMenuItemTriggerType;
	}

	public getItemToggleMode(element: HTMLElement): KTMenuItemToggleType {
		return this._getItemToggleMode(element) as KTMenuItemToggleType;
	}

	public click(element: HTMLElement, event: Event): void {
		this._click(element, event);
	}

	public link(element: HTMLElement, event: Event): void {
		this._link(element, event);
	}

	public dismiss(element: HTMLElement): void {
		this._dismiss(element);
	}

	public mouseover(element: HTMLElement): void {
		this._mouseover(element);
	}

	public mouseout(element: HTMLElement): void {
		this._mouseout(element);
	}

	public show(itemElement: HTMLElement): void {
		return this._show(itemElement);
	}

	public hide(itemElement: HTMLElement): void {
		this._hide(itemElement);
	}

	public toggle(itemElement: HTMLElement): void {
		this._toggle(itemElement);
	}

	public reset(itemElement: HTMLElement): void {
		this._reset(itemElement);
	}

	public update(): void {
		this._update();
	}

	public setActiveLink(link: HTMLElement): void {
		this._setActiveLink(link);
	}

	public getLinkByAttribute(value: string, name: string = 'href'): HTMLElement {
		return this._getLinkByAttribute(value, name);
	}

	public getItemLinkElement(itemElement: HTMLElement): HTMLElement {
		return this._getItemLinkElement(itemElement);
	}

	public getItemElement(element: HTMLElement): HTMLElement {
		return this._getItemElement(element);
	}

	public getItemSubElement(itemElement: HTMLElement): HTMLElement {
		return this._getItemSubElement(itemElement);
	}

	public getItemParentElements(
		itemElement: HTMLElement
	): Array<HTMLElement> | null {
		return this._getItemParentElements(itemElement);
	}

	public isItemSubShown(itemElement: HTMLElement): boolean {
		return this._isItemSubShown(itemElement);
	}

	public isItemParentShown(itemElement: HTMLElement): boolean {
		return this._isItemParentShown(itemElement);
	}

	public isItemDropdownPermanent(itemElement: HTMLElement): boolean {
		return this._isItemDropdownPermanent(itemElement);
	}

	public disable(): void {
		this._disabled = true;
	}

	public enable(): void {
		this._disabled = false;
	}

	public hideAccordions(itemElement?: HTMLElement): void {
		this._hideAccordions(itemElement);
	}

	// Statics methods
	public static getInstance(element: HTMLElement): KTMenu {
		if (!element) {
			return null;
		}

		// Element has menu DOM reference in it's DATA storage
		if (KTData.has(element, 'menu')) {
			return KTData.get(element, 'menu') as KTMenu;
		}

		// Element has .kt-menu parent
		const menuElement = element.closest('[data-kt-menu]') as HTMLElement;
		if (menuElement && KTData.has(menuElement, 'menu')) {
			return KTData.get(menuElement, 'menu') as KTMenu;
		} else if (
			menuElement &&
			menuElement.getAttribute('data-kt-menu') === 'true'
		) {
			return new KTMenu(menuElement);
		}

		const subElement = element.closest(
			'[data-kt-menu-sub-overflow="true"]'
		) as HTMLElement;
		if (subElement && KTData.has(subElement, 'menu')) {
			return KTData.get(subElement, 'menu') as KTMenu;
		}

		// Element has a parent with DOM reference to .kt-menu in it's DATA storage
		if (
			element.classList.contains('kt-menu-link') ||
			element.classList.contains('kt-menu-toggle')
		) {
			const subElement = (element.closest('.kt-menu-dropdown') ||
				element.closest('.kt-menu-accordion')) as HTMLElement;

			if (KTData.has(subElement, 'menu')) {
				return KTData.get(subElement, 'menu') as KTMenu;
			}
		}

		return null;
	}

	public static getOrCreateInstance(
		element: HTMLElement,
		config?: KTMenuConfigInterface
	): KTMenu {
		return this.getInstance(element) || new KTMenu(element, config);
	}

	public static hide(skipElement?: HTMLElement): void {
		const itemElements = document.querySelectorAll(
			'.show.kt-menu-item-dropdown[data-kt-menu-item-trigger]'
		);

		itemElements.forEach((itemElement) => {
			const menu = KTMenu.getInstance(itemElement as HTMLElement);

			if (
				menu &&
				menu.getItemToggleMode(itemElement as HTMLElement) === 'dropdown'
			) {
				if (skipElement) {
					if (
						itemElement &&
						menu
							.getItemSubElement(itemElement as HTMLElement)
							?.contains(skipElement) === false &&
						itemElement.contains(skipElement) === false &&
						itemElement !== skipElement
					) {
						menu.hide(itemElement as HTMLElement);
					}
				} else {
					menu.hide(itemElement as HTMLElement);
				}
			}
		});
	}

	public static updateDropdowns(): void {
		const itemElements = document.querySelectorAll(
			'.show.kt-menu-item-dropdown[data-kt-menu-item-trigger]'
		);

		itemElements.forEach((itemElement) => {
			if (KTData.has(itemElement as HTMLElement, 'popper')) {
				(
					KTData.get(itemElement as HTMLElement, 'popper') as PopperInstance
				).forceUpdate();
			}
		});
	}

	public static updateByLinkAttribute(
		value: string,
		name: string = 'href'
	): void {
		const elements = document.querySelectorAll('[data-kt-menu]');

		elements.forEach((element) => {
			const menu = KTMenu.getInstance(element as HTMLElement);

			if (menu) {
				const link = menu.getLinkByAttribute(value, name);
				if (link) {
					menu.setActiveLink(link);
				}
			}
		});
	}

	public static handleClickAway() {
		document.addEventListener('click', function (event: Event) {
			const itemElements = document.querySelectorAll(
				'.show.kt-menu-item-dropdown[data-kt-menu-item-trigger]:not([data-kt-menu-item-static="true"])'
			);

			itemElements.forEach((itemElement) => {
				const menu = KTMenu.getInstance(itemElement as HTMLElement);

				if (
					menu &&
					menu.getItemToggleMode(itemElement as HTMLElement) === 'dropdown'
				) {
					const subElement = menu.getItemSubElement(itemElement as HTMLElement);

					if (
						itemElement === event.target ||
						itemElement.contains(event.target as HTMLElement)
					) {
						return;
					}

					if (
						subElement &&
						(subElement === event.target ||
							subElement.contains(event.target as HTMLElement))
					) {
						return;
					}

					menu.hide(itemElement as HTMLElement);
				}
			});
		});
	}

	public static findFocused() {
		const linkElement = document.querySelector(
			'.kt-menu-link:focus,.kt-menu-toggle:focus'
		) as HTMLElement;

		if (linkElement && KTDom.isVisible(linkElement)) {
			return linkElement;
		} else {
			return null;
		}
	}

	public static getFocusLink(
		linkElement: HTMLElement,
		direction: 'next' | 'previouse',
		preFocus = false
	): HTMLElement {
		if (!linkElement) return null;

		const itemElement = linkElement.parentElement as HTMLElement;
		if (!itemElement || !itemElement.classList.contains('kt-menu-item'))
			return null;

		if (direction === 'next') {
			const nextElement = linkElement.nextElementSibling as HTMLElement;

			if (
				nextElement &&
				(nextElement.matches(
					'.kt-menu-accordion' + (!preFocus ? '.show' : '')
				) ||
					nextElement.matches('.kt-menu-dropdown' + (!preFocus ? '.show' : '')))
			) {
				const itemElement2 = KTDom.child(
					nextElement,
					'.kt-menu-item'
				) as HTMLElement;
				return KTDom.child(itemElement2, '.kt-menu-link') as HTMLElement;
			} else {
				const nextElement2 = itemElement.nextElementSibling as HTMLElement;

				if (nextElement2 && nextElement2.classList.contains('kt-menu-item')) {
					const nextLink = KTDom.child(
						nextElement2,
						'.kt-menu-link'
					) as HTMLElement;
					if (nextLink) {
						return nextLink;
					}
				}
			}
		} else {
			const prevElement = itemElement.previousElementSibling as HTMLElement;
			if (prevElement) {
				if (prevElement && prevElement.classList.contains('kt-menu-item')) {
					const nextLink = KTDom.child(
						prevElement,
						'.kt-menu-link'
					) as HTMLElement;
					if (nextLink) {
						return nextLink;
					}
				}
			} else {
				const parentElement = itemElement.parentElement as HTMLElement;
				if (
					parentElement &&
					(parentElement.matches(
						'.kt-menu-accordion' + (!preFocus ? '.show' : '')
					) ||
						parentElement.matches(
							'.kt-menu-dropdown' + (!preFocus ? '.show' : '')
						))
				) {
					const prevElement2 =
						parentElement.previousElementSibling as HTMLElement;
					if (prevElement2.classList.contains('kt-menu-link')) {
						return prevElement2;
					}
				}
			}
		}

		return null;
	}

	public static handleKeyboard() {
		document.addEventListener(
			'keydown',
			(event: KeyboardEvent) => {
				if (
					KT_ACCESSIBILITY_KEYS.includes(event.key) &&
					!(event.ctrlKey || event.altKey || event.shiftKey)
				) {
					const currentFocused = this.findFocused() as HTMLElement;
					if (!currentFocused) return;

					if (
						['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight'].includes(
							event.key
						)
					) {
						const direction = ['ArrowDown', 'ArrowRight'].includes(event.key)
							? 'next'
							: 'previouse';
						const newFocusLink = this.getFocusLink(currentFocused, direction);
						event.preventDefault();

						if (newFocusLink) {
							newFocusLink.focus();
							newFocusLink.classList.add('focus');
						}
					}

					if (event.key === 'Enter') {
						const menu = this.getInstance(currentFocused);
						const itemElement = menu.getItemElement(currentFocused);
						const subShown = menu.isItemSubShown(itemElement);

						if (!menu) return;

						if (menu.getItemToggleMode(itemElement) === 'accordion') {
							currentFocused.dispatchEvent(
								new MouseEvent('click', {
									bubbles: true,
								})
							);
						}

						if (menu.getItemToggleMode(itemElement) === 'dropdown') {
							if (menu.getItemTriggerMode(itemElement) === 'click') {
								currentFocused.dispatchEvent(
									new MouseEvent('click', {
										bubbles: true,
									})
								);
							} else {
								if (subShown) {
									currentFocused.dispatchEvent(
										new MouseEvent('mouseout', {
											bubbles: true,
										})
									);
								} else {
									currentFocused.dispatchEvent(
										new MouseEvent('mouseover', {
											bubbles: true,
										})
									);
								}
							}
						}

						if (subShown) {
							const subFocus = this.getFocusLink(currentFocused, 'next', true);
							if (subFocus) {
								subFocus.focus();
							}
						} else {
							currentFocused.focus();
						}

						event.preventDefault();
					}

					if (event.key === 'Escape') {
						const items = document.querySelectorAll(
							'.show.kt-menu-item-dropdown[data-kt-menu-item-trigger]:not([data-kt-menu-item-static="true"])'
						);
						items.forEach((item) => {
							const menu = KTMenu.getInstance(item as HTMLElement);

							if (
								menu &&
								menu.getItemToggleMode(item as HTMLElement) === 'dropdown'
							) {
								menu.hide(item as HTMLElement);
							}
						});
					}
				}
			},
			false
		);
	}

	public static handleMouseover() {
		KTEventHandler.on(
			document.body,
			'[data-kt-menu-item-trigger], .kt-menu-dropdown',
			'mouseover',
			(event: Event, target: HTMLElement) => {
				const menu = KTMenu.getInstance(target);

				if (menu !== null && menu.getItemToggleMode(target) === 'dropdown') {
					return menu.mouseover(target);
				}
			}
		);
	}

	public static handleMouseout() {
		KTEventHandler.on(
			document.body,
			'[data-kt-menu-item-trigger], .kt-menu-dropdown',
			'mouseout',
			(event: Event, target: HTMLElement) => {
				const menu = KTMenu.getInstance(target);

				if (menu !== null && menu.getItemToggleMode(target) === 'dropdown') {
					return menu.mouseout(target);
				}
			}
		);
	}

	public static handleClick() {
		KTEventHandler.on(
			document.body,
			'.kt-menu-item[data-kt-menu-item-trigger] > .kt-menu-link, .kt-menu-item[data-kt-menu-item-trigger] > .kt-menu-label .kt-menu-toggle, .kt-menu-item[data-kt-menu-item-trigger] > .kt-menu-toggle, [data-kt-menu-item-trigger]:not(.kt-menu-item):not([data-kt-menu-item-trigger="auto"])',
			'click',
			(event: Event, target: HTMLElement) => {
				const menu = KTMenu.getInstance(target);

				if (menu !== null) {
					return menu.click(target, event);
				}
			}
		);

		KTEventHandler.on(
			document.body,
			'.kt-menu-item:not([data-kt-menu-item-trigger]) > .kt-menu-link',
			'click',
			(event: Event, target: HTMLElement) => {
				const menu = KTMenu.getInstance(target);

				if (menu !== null) {
					if (target.tagName == 'a' || target.hasAttribute('href')) {
						menu.dismiss(target);
					}
					return menu.link(target, event);
				}
			}
		);
	}

	public static handleDismiss() {
		KTEventHandler.on(
			document.body,
			'[data-kt-menu-dismiss="true"]',
			'click',
			(event: Event, target: HTMLElement) => {
				const menu = KTMenu.getInstance(target);

				if (menu !== null) {
					return menu.dismiss(target);
				}
			}
		);
	}

	public static handleResize() {
		window.addEventListener('resize', () => {
			let timer;

			KTUtils.throttle(
				timer,
				() => {
					// Locate and update Offcanvas instances on window resize
					const elements = document.querySelectorAll('[data-kt-menu]');

					elements.forEach((element) => {
						KTMenu.getInstance(element as HTMLElement)?.update();
					});
				},
				200
			);
		});
	}

	public static initHandlers(): void {
		this.handleDismiss();
		this.handleClickAway();
		this.handleKeyboard();
		this.handleMouseover();
		this.handleMouseout();
		this.handleClick();
		this.handleResize();
	}

	/**
	 * Creates menu instances for all menu elements in the DOM.
	 * This method finds all elements with [data-kt-menu] attribute and initializes them.
	 */
	public static createInstances(): void {
		const elements = document.querySelectorAll(
			'[data-kt-menu]:not([data-kt-menu=false])'
		);
		elements.forEach((element) => {
			new KTMenu(element as HTMLElement);
		});
	}

	/**
	 * Initializes the menu system.
	 * Creates menu instances and sets up event handlers.
	 * Note: Event handlers are only set up once due to global initialization flag.
	 * This can cause issues with Livewire navigation where new elements need handlers.
	 */
	/**
	 * Sets up event handlers for menu interactions.
	 * This method can be called multiple times safely.
	 */
	public static setupHandlers(): void {
		KTMenu.initHandlers();
	}

	/**
	 * Initializes the menu system with proper cleanup and re-initialization support.
	 * This method handles both initial load and Livewire navigation scenarios.
	 */
	public static init(): void {
		// Clean up existing instances to prevent memory leaks
		KTMenu.cleanup();

		// Create new menu instances
		KTMenu.createInstances();

		// Always set up handlers to ensure new elements have event listeners
		KTMenu.setupHandlers();
	}

	/**
	 * Cleans up existing menu instances before re-initialization.
	 * This method prevents memory leaks and duplicate event listeners.
	 *
	 * @param skipElement - Optional element to skip during cleanup
	 */
	public static cleanup(skipElement?: HTMLElement): void {
		const elements = document.querySelectorAll('[data-kt-menu]');
		elements.forEach((element) => {
			if (skipElement && element === skipElement) {
				return; // Skip this element
			}

			const menu = KTMenu.getInstance(element as HTMLElement);
			if (menu) {
				menu.destroy();
			}
		});
	}

	/**
	 * Destroys a specific menu instance and cleans up its resources.
	 * This method properly disposes of menu instances to prevent memory leaks.
	 */
	public destroy(): void {
		// Dispose Popper instance if it exists
		if (KTData.has(this._element, 'popper')) {
			const popper = KTData.get(this._element, 'popper') as PopperInstance;
			if (popper && typeof popper.destroy === 'function') {
				popper.destroy();
			}
			KTData.remove(this._element, 'popper');
		}

		// Remove data attributes and references
		KTData.remove(this._element, this._name);

		// Remove menu reference from sub-elements
		const subElements = this._element.querySelectorAll('.kt-menu-dropdown, .kt-menu-accordion');
		subElements.forEach((subElement) => {
			KTData.remove(subElement as HTMLElement, 'menu');
			KTData.remove(subElement as HTMLElement, 'item');
		});

		// Remove overflow elements from body
		const overflowElements = document.querySelectorAll('[data-kt-menu-sub-overflow="true"]');
		overflowElements.forEach((overflowElement) => {
			if (overflowElement.parentNode === document.body) {
				document.body.removeChild(overflowElement);
			}
		});
	}
}
