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

import { BaseComponent } from './base-component';
import { onTabActivation, activateTab } from './tabs';

/**
 * A component that handles switching between different installation code examples.
 *
 * This component manages tabs that display different code snippets based on the preferred
 * installation method. It persists the user's choice in localStorage and synchronizes
 * the state across browser tabs.
 */
export class CodeSwitcher extends BaseComponent {
	/**
	 * Creates the CodeSwitcher component.
	 *
	 * @param element - The root element of the code switcher.
	 */
	constructor( element ) {
		super();

		this.elements = {
			switcher: element,
			tabs: element.querySelector( '.js-tabs' )
		};

		this.config = {
			storageSwitcherKey: element.getAttribute( 'data-storage-switcher-key' )
		};

		this.state = {
			activeTab: this._getSavedTab() ?? this.elements.tabs.getAttribute( 'data-active-tab-id' )
		};
	}

	/**
	 * Returns the tabs controller for the code switcher.
	 */
	get tabs() {
		return this.elements.tabs.tabs;
	}

	/**
	 * Attaches the CodeSwitcher component to all matching elements on the page.
	 * It initializes the component for each element and sets up event listeners.
	 * This method should be called when the DOM is fully loaded.
	 */
	static attach() {
		const switchers = document.querySelectorAll( '.c-code-switcher' );

		switchers.forEach( switcher => {
			switcher.codeSwitcher = new CodeSwitcher( switcher ).init();
		} );
	}

	/**
	 * Initializes the component by setting up event listeners and synchronizing the state.
	 */
	init() {
		const { tabs } = this.elements;

		window.addEventListener( 'storage', () => this._syncWithSavedTab() );
		onTabActivation( tabs, newTab => {
			this._saveTab( newTab );
			this.tabs
				.findTabPanelByTabId( newTab )
				.querySelector( '.c-code-block' ).codeBlock.updateSize();
		} );

		this._syncWithSavedTab();
		this.render();

		return this;
	}

	/**
	 * Renders the component based on the current state.
	 *
	 * @param prevState - The previous state of the component.
	 * @param state - The current state of the component.
	 */
	render( prevState, state = this.state ) {
		if ( prevState?.activeTab !== state.activeTab ) {
			activateTab( this.elements.tabs, state.activeTab );
		}
	}

	/**
	 * Sets the status of a tab (e.g., 'loading', 'success')
	 *
	 * @param tabId - The ID of the tab to update.
	 * @param status - The status to set for the tab.
	 */
	setTabStatus( tabId, status ) {
		this.tabs
			.findTabPanelByTabId( tabId )
			.querySelector( '.c-code-block' )
			.codeBlock.setStatus( status );

		return this;
	}

	/**
	 * Sets the status of all tabs to a specified value.
	 */
	setTabsStatus( status ) {
		this.tabs.tabsPanels.forEach( tab => {
			tab
				.querySelector( '.c-code-block' )
				.codeBlock.setStatus( status );
		} );

		return this;
	}

	/**
	 * Sets the active tab and updates the code snippet displayed in the tab.
	 *
	 * @param tabId - The ID of the tab to activate.
	 * @param code - The code snippet to display in the activated tab.
	 */
	async setCode( tabId, code ) {
		const tab = this.tabs.findTabPanelByTabId( tabId );

		await tab
			.querySelector( '.c-code-block' )
			.codeBlock.setCode( code );

		return this;
	}

	/**
	 * Synchronizes the component's state with the saved tab in localStorage.
	 */
	_syncWithSavedTab() {
		const savedTab = this._getSavedTab();

		if ( savedTab ) {
			this.setState( {
				activeTab: savedTab
			} );
		}
	}

	/**
	 * Saves the selected tab to localStorage and triggers a storage event.
	 *
	 * @param tabId - The tab ID to save.
	 */
	_saveTab( tabId ) {
		const { storageSwitcherKey } = this.config;

		if ( tabId === this._getSavedTab() || !storageSwitcherKey ) {
			return;
		}

		localStorage.setItem( storageSwitcherKey, tabId );
		window.dispatchEvent( new Event( 'storage' ) );
	}

	/**
	 * Retrieves the currently selected tab from localStorage.
	 * The currently selected tab ID. It's null if not set.
	 */
	_getSavedTab() {
		const { storageSwitcherKey } = this.config;

		if ( !storageSwitcherKey ) {
			return null;
		}

		return localStorage.getItem( storageSwitcherKey );
	}
}
