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

/**
 * Checks if a stored item has expired.
 * Returns true if the item has an expiration timestamp and it's in the past.
 */
const isExpired = parsedValue => {
	if ( parsedValue && typeof parsedValue === 'object' && parsedValue.__expires ) {
		const now = Date.now();
		return parsedValue.__expires < now;
	}

	return false;
};

/**
 * Wraps a value with expiration metadata.
 */
const wrapWithExpiration = ( value, expire ) => {
	if ( expire === null ) {
		return value;
	}

	const expirationTime = expire instanceof Date ?
		expire.getTime() :
		Date.now() + expire;

	return {
		__data: value,
		__expires: expirationTime
	};
};

/**
 * Unwraps a value from its expiration container.
 */
const unwrapFromExpiration = parsedValue => {
	if ( parsedValue && typeof parsedValue === 'object' && parsedValue.__expires ) {
		return parsedValue.__data;
	}

	return parsedValue;
};

/**
 * Storage helper for safely interacting with localStorage with automatic JSON parsing.
 * Handles potential exceptions when localStorage is disabled or unavailable.
 */
export const localStorageHelper = {
	/**
	 * Safely retrieves and parses an item from localStorage.
	 * Checks for expiration and removes expired items.
	 *
	 * @param key - The key to retrieve
	 * @param options - Additional options
	 */
	get( key, { parseJson = true } = {} ) {
		try {
			const value = localStorage.getItem( key );

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

			if ( !parseJson ) {
				return value;
			}

			try {
				const parsedValue = JSON.parse( value );

				// Check if item has expired
				if ( isExpired( parsedValue ) ) {
					localStorage.removeItem( key );
					return null;
				}

				// Return the actual data without the expiration wrapper
				return unwrapFromExpiration( parsedValue );
			} catch ( parseError ) {
				console.warn( 'Error parsing JSON from localStorage:', parseError );

				// Not valid JSON, return as string
				return value;
			}
		} catch ( error ) {
			console.warn( 'Error accessing localStorage:', error );
			return null;
		}
	},

	/**
	 * Safely sets an item in localStorage, with automatic JSON stringification.
	 * Optionally sets an expiration time.
	 *
	 * @param key - The key to set
	 * @param value - The value to store (will be stringified if not a string)
	 * @param options - Additional options
	 */
	set( key, value, { expire = null } = {} ) {
		try {
			const dataToStore = wrapWithExpiration( value, expire );

			const stringValue = typeof dataToStore === 'string' ?
				dataToStore :
				JSON.stringify( dataToStore );

			localStorage.setItem( key, stringValue );
			return true;
		} catch ( error ) {
			console.warn( 'Error writing to localStorage:', error );
			return false;
		}
	},

	/**
	 * Safely removes an item from localStorage.
	 *
	 * @param key - The key to remove
	 */
	remove( key ) {
		try {
			localStorage.removeItem( key );
			return true;
		} catch ( error ) {
			console.warn( 'Error removing from localStorage:', error );
			return false;
		}
	}
};
