/**
 * @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 { localStorageHelper } from '../helpers/local-storage-helper';
import { isUnsafeProperty } from '../helpers/is-unsafe-key';
import { Weeks } from '../helpers/date-time';

/**
 * Remember about renaming inline script in the header bar template.
 */
const LOCAL_STORAGE_KEY = 'gloria-closed-header-bars';

/**
 * Component for managing a closeable header bar.
 * Handles storing closed state in localStorage and toggling visibility.
 */
export class HeaderBar extends BaseComponent {
	/**
	 * Creates a header bar component.
	 *
	 * @param element - The header bar DOM element
	 */
	constructor( element ) {
		super( {
			isHidden: false
		} );

		this.elements = {
			wrapper: element,
			closeButton: element.querySelector( '.c-header-bar__close' )
		};

		this.stateKey = element.dataset.stateStorageKey;

		// Skip initialization if no valid state key
		if ( this.isCloseable && isUnsafeProperty( this.stateKey ) ) {
			throw new Error( 'Header bar has no valid state key:', element );
		}
	}

	/**
	 * Creates header bar components for all matching elements on the page.
	 */
	static attach() {
		const headerBarElements = document.querySelectorAll( '.js-header-bar' );

		for ( const element of headerBarElements ) {
			new HeaderBar( element ).init();
		}
	}

	/**
	 * Checks if the header bar is closeable.
	 */
	get isCloseable() {
		return !!this.stateKey && this.elements.closeButton;
	}

	/**
	 * Initializes the header bar component.
	 * Loads the initial state from localStorage and binds events.
	 */
	init() {
		if ( !this.isCloseable ) {
			return;
		}

		const closedBars = localStorageHelper.get( LOCAL_STORAGE_KEY ) || {};
		const isHidden = !!closedBars[ this.stateKey ];

		this.setState( { isHidden } );
		this.elements.closeButton.addEventListener( 'click', () => this.close() );
	}

	/**
	 * Renders the component based on current state.
	 * Updates the visibility of the header bar.
	 *
	 * @param prevState - Previous component state
	 * @param state - Current component state
	 */
	render( prevState, state ) {
		if ( prevState?.isHidden !== state.isHidden ) {
			this.elements.wrapper.classList.toggle( 'is-hidden', state.isHidden );
			this.saveClosedState();
		}
	}

	/**
	 * Load all closed header bars from localStorage.
	 *
	 * @returns Map of closed header bars
	 */
	loadClosedHeaderBars() {
		return localStorageHelper.get( LOCAL_STORAGE_KEY ) || {};
	}

	/**
	 * Save the current closed state to localStorage.
	 */
	saveClosedState() {
		const closedBars = this.loadClosedHeaderBars();

		if ( this.state.isHidden ) {
			closedBars[ this.stateKey ] = true;
		} else {
			delete closedBars[ this.stateKey ];
		}

		localStorageHelper.set( LOCAL_STORAGE_KEY, closedBars, {
			expire: Weeks.to.milliseconds( 2 )
		} );
	}

	/**
	 * Close this header bar and save its state.
	 */
	close() {
		this.setState( { isHidden: true } );
		this.dispatchHeaderChangeEvent();
	}

	/**
	 * Dispatch a custom event to notify other components about the header bar change.
	 */
	dispatchHeaderChangeEvent() {
		const headerChangeEvent = new CustomEvent( 'header-bar:update', {
			bubbles: true,
			detail: { text: () => 'Header bar was removed.' }
		} );

		document.dispatchEvent( headerChangeEvent );
	}
}

/**
 * Attaches a listener to the header bar update event.
 */
export function onHeaderBarStateChange( callback ) {
	const handler = event => {
		if ( event.detail.text() === 'Header bar was removed.' ) {
			callback();
		}
	};

	document.addEventListener( 'header-bar:update', handler );

	return () => {
		document.removeEventListener( 'header-bar:update', handler );
	};
}
