export const BREAKPOINT_ORDER = [ 'pc', 'md', 'tb', 'sp' ];

export const DEFAULT_BREAKPOINTS = {
	enabled: [ 'pc', 'md', 'tb', 'sp' ],
	values: {
		lg: 1280,
		md: 1140,
		tb: 840,
		sp: 540,
		mn: 320,
	},
};

export const BREAKPOINT_LABELS = {
	lg: 'Large Desktop',
	pc: 'Desktop',
	md: 'Medium',
	tb: 'Tablet',
	sp: 'Mobile',
	mn: 'Mini',
};

export const getBreakpointConfig = () => {
	if ( typeof window !== 'undefined' && window.zenBlocks?.breakpoints ) {
		return window.zenBlocks.breakpoints;
	}
	return DEFAULT_BREAKPOINTS;
};

export const getEnabledBreakpoints = () => {
	const config = getBreakpointConfig();
	return config.enabled || DEFAULT_BREAKPOINTS.enabled;
};

export const getBreakpointValue = ( key ) => {
	const config = getBreakpointConfig();
	return config.values?.[ key ] || DEFAULT_BREAKPOINTS.values[ key ];
};

export const getInheritedValue = ( values, breakpoint ) => {
	const enabled = getEnabledBreakpoints();
	const currentIndex = enabled.indexOf( breakpoint );

	if ( currentIndex === -1 ) {
		return values.pc;
	}

	for ( let i = currentIndex; i >= 0; i-- ) {
		const bp = enabled[ i ];
		if (
			values[ bp ] !== undefined &&
			values[ bp ] !== '' &&
			values[ bp ] !== null
		) {
			return values[ bp ];
		}
	}

	return values.pc;
};

export const shouldOutputBreakpoint = ( values, breakpoint ) => {
	const enabled = getEnabledBreakpoints();
	const currentIndex = enabled.indexOf( breakpoint );

	if ( currentIndex === 0 ) {
		return true;
	}

	const currentValue = values[ breakpoint ];
	if (
		currentValue === undefined ||
		currentValue === '' ||
		currentValue === null
	) {
		return false;
	}

	const inheritedValue = getInheritedValue(
		values,
		enabled[ currentIndex - 1 ]
	);
	return currentValue !== inheritedValue;
};

export const generateMediaQuery = ( breakpoint, type = 'max-width' ) => {
	const value = getBreakpointValue( breakpoint );
	if ( ! value ) {
		return null;
	}
	return `@media (${ type }: ${ value }px)`;
};

export const V2_RESPONSIVE_ATTRIBUTES = [
	'fontSizeV2',
	'fontSizeV2Hover',
	'fontWeightV2',
	'fontWeightV2Hover',
	'lineHeightV2',
	'lineHeightV2Hover',
	'letterSpacingV2',
	'letterSpacingV2Hover',
	'paddingV2',
	'marginV2',
	'borderV2',
	'borderV2Hover',
	'radiusV2',
	'radiusV2Hover',
	'shadowV2',
	'shadowV2Hover',
	'opacityV2',
	'opacityV2Hover',
	'backgroundColorV2',
	'backgroundColorV2Hover',
	'backgroundImageV2',
	'backgroundImageV2Hover',
	'backgroundPatternV2',
	'backgroundPatternV2Hover',
	'iconSizeV2',
	'iconSizeV2Hover',
	'iconPositionV2',
	'iconPositionV2Hover',
	'iconFrameV2',
	'iconFrameV2Hover',
	'layoutWidthV2',
	'layoutHeightV2',
	'layoutPositionV2',
	'thumbnailImageV2',
	'thumbnailImageV2Hover',
	'displayV2',
];

const isEmptyValue = ( value ) => {
	if ( value === undefined || value === null || value === '' ) {
		return true;
	}
	if ( typeof value === 'object' ) {
		return Object.values( value ).every(
			( v ) => v === '' || v === undefined || v === null
		);
	}
	return false;
};

const deepClone = ( obj ) => {
	if ( obj === null || typeof obj !== 'object' ) {
		return obj;
	}
	if ( Array.isArray( obj ) ) {
		return obj.map( deepClone );
	}
	const cloned = {};
	for ( const key in obj ) {
		cloned[ key ] = deepClone( obj[ key ] );
	}
	return cloned;
};

export const migrateV2AttributeForBreakpointChange = (
	attrValue,
	previousBPs,
	newBPs
) => {
	if ( ! attrValue || typeof attrValue !== 'object' ) {
		return attrValue;
	}

	const result = deepClone( attrValue );
	const addedBPs = newBPs.filter( ( bp ) => ! previousBPs.includes( bp ) );
	const removedBPs = previousBPs.filter( ( bp ) => ! newBPs.includes( bp ) );

	addedBPs.forEach( ( addedBP ) => {
		const addedIndex = newBPs.indexOf( addedBP );
		let sourceValue = null;

		for ( let i = addedIndex - 1; i >= 0; i-- ) {
			const upperBP = newBPs[ i ];
			if ( result[ upperBP ] && ! isEmptyValue( result[ upperBP ] ) ) {
				sourceValue = deepClone( result[ upperBP ] );
				break;
			}
		}

		if ( sourceValue ) {
			result[ addedBP ] = sourceValue;
		}
	} );

	removedBPs.forEach( ( removedBP ) => {
		const removedValue = result[ removedBP ];
		if ( ! removedValue || isEmptyValue( removedValue ) ) {
			return;
		}

		const removedIndexInPrev = previousBPs.indexOf( removedBP );
		let targetBP = null;

		for ( let i = removedIndexInPrev + 1; i < previousBPs.length; i++ ) {
			const lowerBP = previousBPs[ i ];
			if ( newBPs.includes( lowerBP ) ) {
				targetBP = lowerBP;
				break;
			}
		}

		if (
			targetBP &&
			( ! result[ targetBP ] || isEmptyValue( result[ targetBP ] ) )
		) {
			result[ targetBP ] = deepClone( removedValue );
		}
	} );

	return result;
};

export const migrateAttributesForBreakpointChange = (
	attributes,
	previousBPs,
	newBPs
) => {
	if ( ! attributes || ! previousBPs || ! newBPs ) {
		return attributes;
	}

	const prevSorted = previousBPs.filter( ( bp ) =>
		BREAKPOINT_ORDER.includes( bp )
	);
	const newSorted = newBPs.filter( ( bp ) =>
		BREAKPOINT_ORDER.includes( bp )
	);

	if ( JSON.stringify( prevSorted ) === JSON.stringify( newSorted ) ) {
		return attributes;
	}

	const migratedAttributes = {};

	V2_RESPONSIVE_ATTRIBUTES.forEach( ( attrName ) => {
		if ( attributes[ attrName ] ) {
			migratedAttributes[ attrName ] =
				migrateV2AttributeForBreakpointChange(
					attributes[ attrName ],
					prevSorted,
					newSorted
				);
		}
	} );

	return migratedAttributes;
};

let previousEnabledBreakpoints = null;

export const getPreviousEnabledBreakpoints = () => previousEnabledBreakpoints;

export const setPreviousEnabledBreakpoints = ( bps ) => {
	previousEnabledBreakpoints = bps ? [ ...bps ] : null;
};

export const initBreakpointTracking = () => {
	const current = getEnabledBreakpoints();
	if ( ! previousEnabledBreakpoints ) {
		previousEnabledBreakpoints = [ ...current ];
	}
	return previousEnabledBreakpoints;
};

export const checkBreakpointChange = () => {
	const current = getEnabledBreakpoints();
	const previous = previousEnabledBreakpoints || current;

	const hasChanged = JSON.stringify( previous ) !== JSON.stringify( current );

	if ( hasChanged ) {
		const result = {
			hasChanged,
			previous: [ ...previous ],
			current: [ ...current ],
		};
		previousEnabledBreakpoints = [ ...current ];
		return result;
	}

	return { hasChanged: false, previous, current };
};
