const clean = ( value ) => String( value ?? '' ).trim();

const finite = ( value ) => {
	const raw = clean( value );
	if ( raw === '' ) return null;
	const number = Number( raw );
	return Number.isFinite( number ) ? number : null;
};

/** Build a shareable locator search URL without disturbing unrelated target params. */
export const buildLocatorSearchUrl = ( target, query, base = 'https://example.invalid/' ) => {
	const fallback = new URL( base );
	let url;
	try {
		url = new URL( target, fallback );
	} catch ( error ) {
		url = fallback;
	}
	// A saved admin value must never become a javascript:/data: navigation.
	if ( ! [ 'http:', 'https:' ].includes( url.protocol ) ) url = fallback;
	const params = url.searchParams;
	[ ...params.keys() ].forEach( ( key ) => {
		if ( key === 'sl-action' || key.startsWith( 'sl-' ) ) params.delete( key );
	} );
	params.set( 'sl-action', 'search' );
	const set = ( key, value ) => {
		if ( value !== undefined && value !== null && clean( value ) !== '' ) params.set( key, clean( value ) );
	};
	set( 'sl-lat', query.location?.lat );
	set( 'sl-lng', query.location?.lng );
	set( 'sl-address', query.location?.address );
	set( 'sl-radius', query.radius );
	set( 'sl-unit', query.unit );
	set( 'sl-city', query.city );
	set( 'sl-country', query.country );
	set( 'sl-category', Array.isArray( query.category ) ? query.category.join( ',' ) : query.category );
	set( 'sl-brand', query.brand );
	set( 'sl-q', query.q );
	if ( query.openNow ) params.set( 'sl-open', '1' );
	set( 'sl-sort', query.sort );
	Object.entries( query.meta || {} ).forEach( ( [ key, value ] ) => {
		if ( ! key || value === undefined || value === '' ) return;
		params.set( `sl-meta[${ key }]`, String( value ) );
		if ( query.metaOps?.[ key ] ) params.set( `sl-metaop[${ key }]`, query.metaOps[ key ] );
	} );
	return url.toString();
};

/** Parse only explicit Store Locator searches; unrelated page query strings are ignored. */
export const readLocatorSearch = ( search = '' ) => {
	const params = new URLSearchParams( String( search ).replace( /^\?/, '' ) );
	if ( params.get( 'sl-action' ) !== 'search' ) return null;
	const patch = {};
	const copy = ( param, key = param.slice( 3 ) ) => {
		const value = clean( params.get( param ) );
		if ( value !== '' ) patch[ key ] = value;
	};
	[ 'sl-city', 'sl-country', 'sl-category', 'sl-brand', 'sl-q' ].forEach( ( key ) => copy( key ) );
	const radius = finite( params.get( 'sl-radius' ) );
	if ( radius !== null && radius > 0 ) patch.radius = radius;
	const unit = params.get( 'sl-unit' );
	if ( unit === 'km' || unit === 'mi' ) patch.unit = unit;
	patch.openNow = params.get( 'sl-open' ) === '1';
	const sort = params.get( 'sl-sort' );
	if ( sort === 'distance' || sort === 'name' ) patch.sort = sort;
	const lat = finite( params.get( 'sl-lat' ) );
	const lng = finite( params.get( 'sl-lng' ) );
	if ( lat !== null && lng !== null && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180 ) {
		patch.location = {
			lat,
			lng,
			source: 'url',
			...( clean( params.get( 'sl-address' ) ) ? { address: clean( params.get( 'sl-address' ) ) } : {} ),
		};
	}
	const meta = {};
	const metaOps = {};
	params.forEach( ( value, key ) => {
		const match = key.match( /^sl-meta\[([a-z0-9_-]+)\]$/i );
		if ( match && clean( value ) ) meta[ match[ 1 ] ] = value;
		const opMatch = key.match( /^sl-metaop\[([a-z0-9_-]+)\]$/i );
		if ( opMatch && clean( value ) ) metaOps[ opMatch[ 1 ] ] = value;
	} );
	if ( Object.keys( meta ).length ) patch.meta = meta;
	if ( Object.keys( metaOps ).length ) patch.metaOps = metaOps;
	return patch;
};

/** Apply a URL search over saved/default query values without dropping meta defaults. */
export const applyLocatorSearch = ( query, search = '' ) => {
	const fromUrl = readLocatorSearch( search );
	if ( ! fromUrl ) return query;
	return {
		...query,
		...fromUrl,
		meta: { ...( query.meta || {} ), ...( fromUrl.meta || {} ) },
		metaOps: { ...( query.metaOps || {} ), ...( fromUrl.metaOps || {} ) },
	};
};
