/**
 * Countdown Pro Extension View Script.
 *
 * Handles Pro features for countdown blocks including:
 * - Evergreen timers with cookie-based persistence
 * - Timer end actions (hide, redirect, replace, zero)
 * - Auto-reload functionality
 * - Reset after days configuration
 *
 * @since x.x.x
 */

/**
 * Internal dependencies.
 */
import { calculateRemainingTime, getUpdateInterval } from '@spectra-helpers/countdown';
import { getEvergreenEndDate, isValidURL, resetEvergreenCookies } from './helpers';

// Import the frontend timer manager from the base countdown block.
let frontendTimerManager;
try {
	// Try to import from the base plugin if available.
	if ( window.spectraFrontendCountdownTimerManager ) {
		frontendTimerManager = window.spectraFrontendCountdownTimerManager;
	}
} catch ( e ) {
	// Timer manager not available, fall back to regular intervals.
}

/**
 * Creates a countdown object with time values and appropriate labels.
 *
 * @since x.x.x
 *
 * @param {Object} time   Time object containing days, hours, minutes, seconds, isExpired, and expiry settings.
 * @param {Object} labels Labels object containing singular and plural forms for each time unit.
 * @return {Object} Formatted countdown object with time values, labels, and expiry settings.
 */
const createCountdownObject = ( time, labels ) => ( {
	days: time.days,
	hours: time.hours,
	minutes: time.minutes,
	seconds: time.seconds,
	isExpired: time.isExpired,
	timerEndAction: time.timerEndAction || 'zero',
	redirectURL: time.redirectURL || '',
	autoReload: time.autoReload || false,
	daysLabel: time.days === 1 ? labels.dayLabel : labels.daysLabel,
	hoursLabel: time.hours === 1 ? labels.hourLabel : labels.hoursLabel,
	minutesLabel: time.minutes === 1 ? labels.minuteLabel : labels.minutesLabel,
	secondsLabel: time.seconds === 1 ? labels.secondLabel : labels.secondsLabel,
} );

/**
 * Updates the DOM elements of a countdown block with current countdown values.
 *
 * @since x.x.x
 *
 * @param {Element} ref       The root DOM element of the countdown block.
 * @param {Object}  countdown The current countdown state object.
 * @return {void}
 */
const updateCountdownDOM = ( ref, countdown ) => {
	const timeUnits = [
		{ type: 'day', value: 'days', label: 'daysLabel' },
		{ type: 'hour', value: 'hours', label: 'hoursLabel' },
		{ type: 'minute', value: 'minutes', label: 'minutesLabel' },
		{ type: 'second', value: 'seconds', label: 'secondsLabel' },
	];

	timeUnits.forEach( ( unit ) => {
		const unitSelector = `.wp-block-spectra-countdown-child-${ unit.type }`;
		const unitElement = ref.querySelector( unitSelector );
		if ( ! unitElement ) return;

		const finalNumber = countdown[ unit.value ] || 0;
		const formattedNumber = String( finalNumber ).padStart( 2, '0' );

		const numberElement = unitElement.querySelector( '.wp-block-spectra-countdown-child-number' );
		// Only update the DOM if the number has changed.
		if ( numberElement && numberElement?.textContent !== formattedNumber ) {
			numberElement.textContent = formattedNumber;
		}

		const label = countdown[ unit.label ];
		const labelElement = unitElement.querySelector( '.wp-block-spectra-countdown-child-label' );
		// Only update the DOM if the label has changed.
		if ( labelElement && labelElement?.textContent !== label ) {
			labelElement.textContent = label;
		}
	} );
};

/**
 * Handles the specified action when the countdown timer expires.
 *
 * @since x.x.x
 *
 * @param {Element} ref     The root DOM element of the countdown block.
 * @param {Object}  context The Interactivity API context containing timer settings.
 * @return {void}
 */
const handleExpiryActions = ( ref, context ) => {
	const { timerEndAction, resetAfterDays, reloadRequired, autoReload, redirectURL, uniqueId } = context;

	// Validate required properties.
	// Timer end action and element reference are required.
	if ( ! timerEndAction || ! ref ) {
		return;
	}

	// Handle expiry actions based on the configured timer end action.
	switch ( timerEndAction ) {
		case 'zero':
			if ( resetAfterDays === 0 && uniqueId && context.timerType === 'evergreen' ) {
				// Clear the cookie to reinitialize the timer (only for evergreen timers).
				resetEvergreenCookies( uniqueId );

				// Clear any existing timer registration first.
				if ( context.unregister ) {
					context.unregister();
					context.unregister = null;
				}

				// Force a complete re-initialization by dispatching the initialized event again.
				// This will trigger the evergreen timer initialization from scratch.
				// The timeout ensures the re-initialization happens after the current execution.
				setTimeout( () => {
					const reinitEvent = new CustomEvent( 'spectra:countdown:initialized', {
						detail: { context },
						bubbles: true,
					} );
					ref.dispatchEvent( reinitEvent );
				}, 10 );
			}
			// For date-based timers, 'zero' means just show zeros (default behavior).
			break;
		case 'hide':
			ref.remove();
			break;
		case 'redirect':
			if ( redirectURL && isValidURL( redirectURL ) ) {
				window.location.href = redirectURL;
			}
			break;
		case 'replace':
			if ( reloadRequired ) {
				if ( autoReload ) {
					// Check if we already have the reverse-replace class or if replacement content is already shown.
					// This prevents infinite reload loops.
					const isAlreadyReplaced =
						ref.classList.contains( 'reverse-replace' ) || ! ref.classList.contains( 'replace' );

					if ( ! isAlreadyReplaced ) {
						setTimeout( () => window.location.reload(), 1000 );
					}
				}
			} else {
				ref.classList.remove( 'replace' );
				ref.classList.add( 'reverse-replace' );
			}
			break;
		default:
		// Default action: do nothing.
	}
};

/**
 * Initializes and manages an evergreen countdown timer.
 *
 * @since x.x.x
 *
 * @param {Element} ref     The root DOM element of the countdown block.
 * @param {Object}  context The Interactivity API context containing timer settings.
 * @return {void}
 */
const initializeEvergreenTimer = ( ref, context ) => {
	const {
		showDays,
		showHours,
		showMinutes,
		showSeconds,
		labels,
		timerType,
		evergreenDays,
		evergreenHours,
		evergreenMinutes,
		uniqueId,
		resetAfterDays = 0,
	} = context;

	// Only handle evergreen timers with valid uniqueId and element reference.
	if ( timerType !== 'evergreen' || ! uniqueId || ! ref ) {
		return;
	}

	// Check if replacement content is already shown (prevents infinite reload loop).
	// If the countdown has 'replace' class removed or has 'reverse-replace' class, timer already expired.
	if (
		context.timerEndAction === 'replace' &&
		( ref.classList.contains( 'reverse-replace' ) || ! ref.classList.contains( 'replace' ) )
	) {
		return;
	}

	// Clear any existing timer registration to prevent duplicate timers.
	if ( context.unregister ) {
		context.unregister();
		context.unregister = null;
	}

	// Clear the free version's interval if it exists for backward compatibility.
	if ( context.intervalId ) {
		clearInterval( context.intervalId );
		context.intervalId = null;
	}

	// Calculate evergreen end date based on the configured duration.
	const evergreenEndDateTime = getEvergreenEndDate(
		evergreenDays,
		evergreenHours,
		evergreenMinutes,
		uniqueId,
		resetAfterDays
	);

	if ( ! evergreenEndDateTime || isNaN( new Date( evergreenEndDateTime ).getTime() ) ) {
		return;
	}

	// Create unique ID for this evergreen countdown instance.
	const countdownId = `evergreen-${ uniqueId || Math.random().toString( 36 ).substr( 2, 9 ) }`;

	// Use global timer manager if available, otherwise fall back to regular intervals.
	if ( frontendTimerManager ) {
		// Register with global timer manager for better performance.
		context.unregister = frontendTimerManager.register( countdownId, {
			element: ref,
			endDateTime: evergreenEndDateTime,
			showDays,
			showHours,
			showMinutes,
			showSeconds,
			labels,
			onExpired: () => {
				const expiredEvent = new CustomEvent( 'spectra:countdown:expired', {
					detail: { context },
					bubbles: true,
				} );
				ref.dispatchEvent( expiredEvent );
			},
		} );
	} else {
		// Fallback to regular interval if timer manager not available.
		// This ensures backward compatibility with older base plugin versions.
		const intervalDuration = getUpdateInterval( showDays, showHours, showMinutes, showSeconds );

		context.intervalId = setInterval( () => {
			const time = calculateRemainingTime( evergreenEndDateTime, showDays, showHours, showMinutes, showSeconds );

			const countdown = createCountdownObject( time, labels );

			context.countdown = countdown;
			context.endDateTime = time.endDateTime; // Update endDateTime in context.

			updateCountdownDOM( ref, countdown );

			// Apply expiry actions and stop interval if expired.
			if ( countdown.isExpired ) {
				// Clear interval to prevent further updates after expiry.
				if ( context.intervalId ) {
					clearInterval( context.intervalId );
					context.intervalId = null;
				}

				const expiredEvent = new CustomEvent( 'spectra:countdown:expired', {
					detail: { context },
					bubbles: true,
				} );
				ref.dispatchEvent( expiredEvent );
			}
		}, intervalDuration );
	}
};

/**
 * Event listener for countdown initialization.
 * Handles the 'spectra:countdown:initialized' event to set up evergreen timers.
 *
 * @since x.x.x
 *
 * @property {Object}  detail Event details containing the context.
 * @property {Element} target The countdown block element.
 */
document.addEventListener( 'spectra:countdown:initialized', ( event ) => {
	const { context } = event.detail;
	const ref = event.target;

	initializeEvergreenTimer( ref, context );
} );

/**
 * Event listener for countdown expiration.
 * Handles the 'spectra:countdown:expired' event to perform expiry actions.
 *
 * @since x.x.x
 *
 * @property {Object}  detail Event details containing the context.
 * @property {Element} target The countdown block element.
 */
document.addEventListener( 'spectra:countdown:expired', ( event ) => {
	const { context } = event.detail;
	const ref = event.target;

	// Apply expiry actions based on the timer configuration.
	handleExpiryActions( ref, context );
} );
