/**
 * @license Copyright (c) 2017-2025, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md.
 */

import { BaseComponent } from '../components/base-component';
import { toggleHeight } from '../helpers/toggle-height';
import { getParentsUntil } from '../helpers/get-parents-until';
import { onMobileOverlayStateChange } from '../components/mobile-overlay';

export class ApiNavTree extends BaseComponent {
	/**
	 * Creates an API navigation tree component.
	 *
	 * @param element - The navigation tree element.
	 */
	constructor( element ) {
		super( {
			isMobileApiNavInitialized: false
		} );

		this.elements = {
			navTree: element,
			sidebarNav: element.parentElement
		};
	}

	/**
	 * Initializes navigation tree component on the page.
	 */
	static attach() {
		const navTrees = document.querySelectorAll( '.js-api-tree' );

		navTrees.forEach( navTree => {
			new ApiNavTree( navTree ).init();
		} );
	}

	/**
	 * Initializes the navigation tree component with event listeners.
	 */
	init() {
		const { navTree, sidebarNav } = this.elements;

		navTree?.addEventListener( 'click', async event => {
			const button = event.target.closest( 'button' ) || event.target.closest( 'span' );

			sessionStorage.setItem( 'ckApiSidebarScroll', sidebarNav.scrollTop );

			if ( !button || button.tagName === 'A' ) {
				return;
			}

			const toggleButton = button.tagName === 'BUTTON' ? button : button.previousElementSibling;

			if ( !toggleButton ) {
				return;
			}

			toggleButton.classList.toggle( 'api-tree__button--open' );

			const nestedList = button.parentElement.parentElement.querySelector( '.api-tree__list' );

			if ( !nestedList ) {
				return;
			}

			const toggleResult = await toggleHeight( nestedList, 'api-tree__list--hidden' );

			if ( toggleResult === 'visible' ) {
				nestedList.style.display = 'block';
			} else {
				nestedList.style.display = 'none';
			}
		} );

		this.expandWhereYouAre();
		this.render();

		if ( this.state.isMobileApiNavInitialized ) {
			return;
		}

		// Add listener for mobile overlay opening - to copy API sidebar to mobile view.
		onMobileOverlayStateChange( {
			eventName: 'opening',
			callback: () => {
				const desktopApiNav = document.querySelector( '#js-api-tree-desktop' );
				const desktopApiNavClone = desktopApiNav.cloneNode( true );
				const mobileApiNav = document.querySelector( '#js-api-tree-mobile' );

				mobileApiNav.appendChild( desktopApiNavClone );

				this.setState( {
					isMobileApiNavInitialized: true
				} );

				new ApiNavTree( mobileApiNav.querySelector( '.js-api-tree' ) ).init();
			}
		} );
	}

	render() {
		this.retrieveScrollbarPosition();
	}

	/**
	 * Expands the navigation tree to show the current page's location.
	 */
	expandWhereYouAre() {
		const { navTree } = this.elements;

		if ( !document.querySelector( '.js-api-h1' ) ) {
			return;
		}

		const activeTreeItemClass = 'active';
		const togglerCollapsedClass = 'api-tree__list--hidden';
		const pageIdentifier = document.querySelector( '.js-api-h1' ).dataset.ln;

		if ( !pageIdentifier || pageIdentifier === 'index' ) {
			return;
		}

		const currentTreeItem = navTree.querySelector( `div[data-ln='${ pageIdentifier }']` );
		const closestParent = currentTreeItem.closest( '.api-tree__item-wrapper' );

		closestParent?.classList.add( activeTreeItemClass );

		const ancestors = getParentsUntil( currentTreeItem, '.js-api-tree' )
			.filter( element => element.classList.contains( 'api-tree__list--hidden' ) );
		const siblingsLists = navTree.querySelectorAll( '.api-tree__item-wrapper.active ~ .api-tree__list' );

		[ ...ancestors, ...siblingsLists ].forEach( listItem => {
			listItem.classList.remove( togglerCollapsedClass );
			listItem.style.height = '';
			listItem.style.display = 'block';
		} );

		closestParent?.firstElementChild.classList.add( 'api-tree__button--open' );
	}

	retrieveScrollbarPosition() {
		const { navTree, sidebarNav } = this.elements;

		if ( !sidebarNav.classList.contains( 'c-sidebar__nav' ) ) {
			return;
		}

		// Retrieve the stored scroll position from sessionStorage.
		const storedScrollPosition = sessionStorage.getItem( 'ckApiSidebarScroll' );

		// If a stored scroll position exists, scroll the sidebar to that position.
		if ( storedScrollPosition !== null ) {
			sidebarNav.scrollTop = Number( storedScrollPosition );
		}

		const activeElement = navTree.querySelector( '.api-tree__item-wrapper.active' );

		setTimeout( () => {
			// Ensure the sidebar is scrolled to the active element after a short delay.
			activeElement?.scrollIntoView( {
				behavior: 'instant',
				block: 'nearest',
				inline: 'nearest'
			} );

			sessionStorage.setItem( 'ckApiSidebarScroll', sidebarNav.scrollTop );
		}, 100 );
	}
}
