/* Client-side preview calculation, mirroring the storefront/PHP logic closely
 * enough for an instant, accurate-looking admin preview. The authoritative
 * storefront dates are still computed in PHP. */

function isoDate( d ) {
	return (
		d.getFullYear() +
		'-' + String( d.getMonth() + 1 ).padStart( 2, '0' ) +
		'-' + String( d.getDate() ).padStart( 2, '0' )
	);
}

export function addBusinessDays( base, n, workingDays, holidaySet ) {
	const wd = new Set( ( workingDays && workingDays.length ? workingDays : [ 1, 2, 3, 4, 5 ] ).map( Number ) );
	let d = new Date( base );
	let count = 0;
	let guard = 0;
	while ( count < n && guard < 600 ) {
		d.setDate( d.getDate() + 1 );
		guard++;
		if ( wd.has( d.getDay() ) && ! holidaySet.has( isoDate( d ) ) ) {
			count++;
		}
	}
	return d;
}

const DAY_NAMES = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ];
const MONTH_NAMES = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ];

function ordinal( n ) {
	const s = [ 'th', 'st', 'nd', 'rd' ];
	const v = n % 100;
	return s[ ( v - 20 ) % 10 ] || s[ v ] || s[ 0 ];
}

// Format a Date using a PHP date() format string, so the preview matches the
// storefront's "Date format" setting exactly (d/m/Y, M j Y, D M j, etc.).
function phpDate( d, fmt ) {
	const pad = ( n ) => String( n ).padStart( 2, '0' );
	const map = {
		d: pad( d.getDate() ),
		j: String( d.getDate() ),
		D: DAY_NAMES[ d.getDay() ].slice( 0, 3 ),
		l: DAY_NAMES[ d.getDay() ],
		N: String( d.getDay() === 0 ? 7 : d.getDay() ),
		w: String( d.getDay() ),
		S: ordinal( d.getDate() ),
		m: pad( d.getMonth() + 1 ),
		n: String( d.getMonth() + 1 ),
		M: MONTH_NAMES[ d.getMonth() ].slice( 0, 3 ),
		F: MONTH_NAMES[ d.getMonth() ],
		Y: String( d.getFullYear() ),
		y: String( d.getFullYear() ).slice( -2 ),
	};
	let out = '';
	for ( let i = 0; i < fmt.length; i++ ) {
		const c = fmt[ i ];
		if ( c === '\\' ) { out += fmt[ ++i ] || ''; continue; }
		out += c in map ? map[ c ] : c;
	}
	return out;
}

export function computePreview( settings, rules ) {
	const now = new Date();
	// The store-wide default is the first rule whose every condition is "Any"
	// (empty values) — i.e. it matches every product.
	let def = null;
	if ( rules ) {
		for ( const k of Object.keys( rules ) ) {
			const conds = rules[ k ].conditions || [];
			if ( conds.every( ( c ) => ! ( c.vals && c.vals.length ) ) ) { def = rules[ k ]; break; }
		}
	}
	const hasDefault = !! def;
	const min = Math.max( 1, parseInt( def ? def.min : settings.min_days, 10 ) || 2 );
	const max = Math.max( min, parseInt( def ? def.max : settings.max_days, 10 ) || 5 );
	const workingDays = Array.isArray( settings.working_days ) ? settings.working_days.map( Number ) : [ 1, 2, 3, 4, 5 ];
	const holidaySet = new Set(
		( Array.isArray( settings.skipped_dates ) ? settings.skipped_dates : [] )
			.filter( ( h ) => ( h.type ?? 'cus' ) === 'cus' && h.val && h.on !== 'no' && h.on !== false )
			.map( ( h ) => h.val )
	);

	const cutoff = settings.cutoff_time || '14:00';
	const [ ch, cm ] = cutoff.split( ':' ).map( ( x ) => parseInt( x, 10 ) || 0 );
	const cutoffTime = new Date( now );
	cutoffTime.setHours( ch, cm, 0, 0 );
	const past = now > cutoffTime;
	const off = past ? 1 : 0;

	const start = addBusinessDays( now, min + off, workingDays, holidaySet );
	const end = addBusinessDays( now, max + off, workingDays, holidaySet );

	const dateFormat = settings.date_format_custom || 'M j, Y';
	const template = settings.message_template || 'Estimated delivery: {start} – {end}';
	const message = template
		.replace( /\{start\}/g, phpDate( start, dateFormat ) )
		.replace( /\{end\}/g, phpDate( end, dateFormat ) )
		.replace( /\{days\}/g, min + '–' + max + ' days' );

	let countdown;
	let tone;
	if ( ! past ) {
		const diff = cutoffTime - now;
		const h = Math.floor( diff / 3600000 );
		const m = Math.floor( ( diff % 3600000 ) / 60000 );
		let hr = ch;
		const ap = hr >= 12 ? 'PM' : 'AM';
		hr = hr % 12 || 12;
		countdown = `Order within ${ h }h ${ m }m (by ${ hr }:${ String( cm ).padStart( 2, '0' ) } ${ ap })`;
		tone = '#00854a';
	} else {
		countdown = 'Cutoff passed — ships next business day';
		tone = '#b26200';
	}

	return {
		message,
		countdown,
		tone,
		past,
		hasDefault,
		today: now.toLocaleDateString( 'en-US', { weekday: 'long', month: 'long', day: 'numeric' } ),
	};
}
