/**
 * Search + searchable category filter for lists / library.
 */
import { useMemo } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import SearchableSelect from '../../components/SearchableSelect';

/**
 * Resolve category leaf slugs from a list item (multi or singular).
 *
 * @param {object} item
 * @return {string[]}
 */
export function blockCategorySlugs( item ) {
	const fromList = Array.isArray( item?.categorySlugs )
		? item.categorySlugs
				.map( ( s ) => String( s || '' ).trim() )
				.filter( Boolean )
		: [];
	if ( fromList.length ) {
		return fromList;
	}
	const primary = String( item?.categorySlug || '' ).trim();
	return primary ? [ primary ] : [ 'uncategorized' ];
}

/**
 * Whether a block matches a category filter (leaf slug or parent slug).
 *
 * @param {object} item
 * @param {string} cat
 * @return {boolean}
 */
export function blockMatchesCategory( item, cat ) {
	const filter = String( cat || 'all' ).trim();
	if ( ! filter || filter === 'all' ) {
		return true;
	}
	if ( filter === 'featured' ) {
		return Boolean( item?.isFeatured );
	}
	const slugs = blockCategorySlugs( item );
	if ( slugs.includes( filter ) ) {
		return true;
	}
	const parent = String( item?.categoryParentSlug || '' ).trim();
	if ( parent && parent === filter ) {
		return true;
	}
	return false;
}

/**
 * Build unique category options from list items that expose categorySlug/Name.
 *
 * @param {Array<{ categorySlug?: string, categoryName?: string, categorySlugs?: string[] }>} items
 * @return {Array<{ label: string, value: string }>}
 */
export function categoryOptionsFromItems( items = [] ) {
	const map = new Map();
	( items || [] ).forEach( ( item ) => {
		const slugs = blockCategorySlugs( item );
		slugs.forEach( ( slug ) => {
			if ( ! slug || map.has( slug ) ) {
				return;
			}
			const label =
				slug === String( item?.categorySlug || '' ).trim()
					? String( item?.categoryName || slug ).trim() || slug
					: slug;
			map.set( slug, {
				value: slug,
				label,
			} );
		} );
	} );
	return Array.from( map.values() ).sort( ( a, b ) =>
		a.label.localeCompare( b.label, undefined, { sensitivity: 'base' } )
	);
}

/**
 * Build post type options from block postTypes + optional label map.
 *
 * @param {Array<{ postTypes?: string[] }>} items
 * @param {Array<{ slug?: string, label?: string }>|Record<string, string>} [labels]
 * @return {Array<{ label: string, value: string }>}
 */
export function postTypeOptionsFromItems( items = [], labels = [] ) {
	const labelMap = new Map();
	if ( Array.isArray( labels ) ) {
		labels.forEach( ( type ) => {
			const slug = String( type?.slug || '' ).trim();
			if ( slug ) {
				labelMap.set( slug, String( type?.label || slug ) );
			}
		} );
	} else if ( labels && typeof labels === 'object' ) {
		Object.entries( labels ).forEach( ( [ slug, label ] ) => {
			if ( slug ) {
				labelMap.set( String( slug ), String( label || slug ) );
			}
		} );
	}

	const map = new Map();
	( items || [] ).forEach( ( item ) => {
		( Array.isArray( item?.postTypes ) ? item.postTypes : [] ).forEach(
			( raw ) => {
				const slug = String( raw || '' ).trim();
				if ( ! slug || map.has( slug ) ) {
					return;
				}
				map.set( slug, {
					value: slug,
					label: labelMap.get( slug ) || slug,
				} );
			}
		);
	} );

	return Array.from( map.values() ).sort( ( a, b ) =>
		a.label.localeCompare( b.label, undefined, { sensitivity: 'base' } )
	);
}

/**
 * Flatten library category tree into select options (parents + children).
 *
 * @param {Array<{ slug: string, name: string, children?: Array }>} tree
 * @param {{ includeFeatured?: boolean }} [opts]
 * @return {Array<{ label: string, value: string }>}
 */
export function categoryOptionsFromTree( tree = [], { includeFeatured = true } = {} ) {
	const opts = [];
	if ( includeFeatured ) {
		opts.push( {
			value: 'featured',
			label: `⭐ ${ __( 'Featured', 'joc-blockora-morph' ) }`,
		} );
	}
	( tree || [] ).forEach( ( group ) => {
		if ( ! group?.slug || ( group.blockCount || 0 ) <= 0 ) {
			return;
		}
		opts.push( {
			value: group.slug,
			label: String( group.name || group.slug ),
		} );
		( group.children || [] ).forEach( ( child ) => {
			if ( ! child?.slug || ( child.blockCount || 0 ) <= 0 ) {
				return;
			}
			opts.push( {
				value: child.slug,
				label: `${ group.name || group.slug } › ${ child.name || child.slug }`,
			} );
		} );
	} );
	return opts;
}

/**
 * Filter items by search query + category slug + optional post type.
 *
 * @param {Array<object>} items
 * @param {{ query?: string, category?: string, postType?: string, getHaystack?: (item: object) => string }} opts
 */
export function filterBlocksList(
	items,
	{ query = '', category = 'all', postType = 'all', getHaystack } = {}
) {
	const q = String( query || '' )
		.trim()
		.toLowerCase();
	const cat = String( category || 'all' );
	const type = String( postType || 'all' );

	return ( items || [] ).filter( ( item ) => {
		if ( cat && cat !== 'all' ) {
			if ( ! blockMatchesCategory( item, cat ) ) {
				return false;
			}
		}
		if ( type && type !== 'all' ) {
			const types = Array.isArray( item?.postTypes )
				? item.postTypes.map( String )
				: [];
			if ( ! types.includes( type ) ) {
				return false;
			}
		}
		if ( ! q ) {
			return true;
		}
		const hay =
			typeof getHaystack === 'function'
				? getHaystack( item )
				: `${ item?.name || '' } ${ item?.systemName || '' } ${ item?.categoryName || '' } ${ item?.variant || '' } ${ item?.shortcode || '' } ${ item?.userBlockName || '' }`;
		return String( hay ).toLowerCase().includes( q );
	} );
}

/**
 * @param {object}   props
 * @param {string}   props.query
 * @param {Function} props.onQueryChange
 * @param {string}   props.category
 * @param {Function} props.onCategoryChange
 * @param {Array<{ label: string, value: string }>} props.categoryOptions
 * @param {string}   [props.postType]
 * @param {Function} [props.onPostTypeChange]
 * @param {Array<{ label: string, value: string }>} [props.postTypeOptions]
 * @param {string}   [props.searchPlaceholder]
 * @param {number}   [props.resultCount]
 * @param {number}   [props.totalCount]
 * @param {boolean}  [props.showClear]
 * @param {Function} [props.onClear]
 * @param {boolean}  [props.hideCategoryFilter] Hide the category select (e.g. category drill-down)
 * @param {string}   [props.activeLabel]
 */
export default function BlocksListFilters( {
	query,
	onQueryChange,
	category,
	onCategoryChange,
	categoryOptions = [],
	postType,
	onPostTypeChange,
	postTypeOptions = [],
	searchPlaceholder,
	resultCount,
	totalCount,
	showClear,
	onClear,
	hideCategoryFilter = false,
	activeLabel,
} ) {
	const showPostType =
		typeof onPostTypeChange === 'function' &&
		Array.isArray( postTypeOptions );

	const options = useMemo(
		() => [
			{
				value: 'all',
				label: __( 'All categories', 'joc-blockora-morph' ),
			},
			...categoryOptions,
		],
		[ categoryOptions ]
	);

	const typeOptions = useMemo(
		() => [
			{
				value: 'all',
				label: __( 'All post types', 'joc-blockora-morph' ),
			},
			...postTypeOptions,
		],
		[ postTypeOptions ]
	);

	const hasActiveFilter =
		Boolean( query?.trim() ) ||
		( category && category !== 'all' ) ||
		( showPostType && postType && postType !== 'all' );

	const showCount =
		typeof resultCount === 'number' &&
		typeof totalCount === 'number' &&
		hasActiveFilter;

	const canClear =
		showClear !== false && hasActiveFilter && typeof onClear === 'function';

	return (
		<div
			className={
				'bm-list-filters' +
				( showPostType ? ' bm-list-filters--with-post-type' : '' )
			}
		>
			<label className="bm-list-filters__search group">
				<span className="screen-reader-text">
					{ __( 'Search', 'joc-blockora-morph' ) }
				</span>
				<span
					className="dashicons dashicons-search bm-list-filters__search-icon"
					aria-hidden="true"
				/>
				<input
					type="search"
					value={ query }
					onChange={ ( e ) => onQueryChange( e.target.value ) }
					placeholder={
						searchPlaceholder ||
						__( 'Search by name…', 'joc-blockora-morph' )
					}
					className="bm-list-filters__search-input"
				/>
			</label>

			{ ! hideCategoryFilter ? (
				<div className="bm-list-filters__category">
					<SearchableSelect
						label={ __( 'Category', 'joc-blockora-morph' ) }
						hideLabelFromVision
						value={ category || 'all' }
						onChange={ ( next ) => onCategoryChange( next || 'all' ) }
						options={ options }
					/>
				</div>
			) : null }

			{ showPostType ? (
				<div className="bm-list-filters__post-type">
					<SearchableSelect
						label={ __( 'Post type', 'joc-blockora-morph' ) }
						hideLabelFromVision
						value={ postType || 'all' }
						onChange={ ( next ) =>
							onPostTypeChange( next || 'all' )
						}
						options={ typeOptions }
					/>
				</div>
			) : null }

			{ canClear ? (
				<button
					type="button"
					className="bm-list-filters__clear"
					onClick={ onClear }
				>
					<span
						className="dashicons dashicons-dismiss"
						aria-hidden="true"
					/>
					{ __( 'Clear filters', 'joc-blockora-morph' ) }
				</button>
			) : null }

			{ showCount || ( canClear && activeLabel && category !== 'all' ) ? (
				<div className="bm-list-filters__meta" aria-live="polite">
					{ category && category !== 'all' && activeLabel ? (
						<span className="bm-list-filters__chip">
							{ __( 'Showing', 'joc-blockora-morph' ) }{ ' ' }
							<strong>{ activeLabel }</strong>
						</span>
					) : null }
					{ showCount ? (
						<span className="bm-list-filters__count">
							{ sprintf(
								/* translators: 1: visible count, 2: total count */
								__( 'Showing %1$d of %2$d', 'joc-blockora-morph' ),
								resultCount,
								totalCount
							) }
						</span>
					) : null }
				</div>
			) : null }
		</div>
	);
}
