import { createPortal, useEffect, useMemo, useState } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { api } from '../api';
import BlocksListFilters, {
	categoryOptionsFromItems,
	filterBlocksList,
	blockMatchesCategory,
} from '../components/BlocksListFilters';
import LibraryCardThumb from '../components/LibraryCardThumb';
import LibrarySkeleton from '../components/LibrarySkeleton';
import PreviewFrame from '../components/PreviewFrame';
import PickUserBlockModal from '../components/PickUserBlockModal';
import { renderPreview } from '../components/PreviewModal';
import TemplateUpdateModal from '../components/TemplateUpdateModal';
import ConfirmModal from '../components/ui/ConfirmModal';
import BulkConfirmItemsList from '../components/BulkConfirmItemsList';

function shortcodePreviewBlock( item ) {
	if ( ! item ) {
		return null;
	}
	return {
		markup: item.markup,
		defaultAttrs: item.contentAttrs || {},
		cssVars: item.cssVars || {},
		fieldSchema: item.fieldSchema || [],
	};
}

function ShortcodePreviewModal( { item, onClose, onEdit } ) {
	const preview = useMemo(
		() => {
			const block = shortcodePreviewBlock( item );
			return block
				? renderPreview(
						block.markup,
						block.defaultAttrs,
						block.cssVars,
						block.fieldSchema
				  )
				: { __html: '', styleCss: '' };
		},
		[ item ]
	);

	useEffect( () => {
		if ( ! item ) {
			return undefined;
		}
		const onKey = ( e ) => {
			if ( e.key === 'Escape' ) {
				onClose();
			}
		};
		const prevOverflow = document.body.style.overflow;
		document.body.style.overflow = 'hidden';
		window.addEventListener( 'keydown', onKey );
		return () => {
			document.body.style.overflow = prevOverflow;
			window.removeEventListener( 'keydown', onKey );
		};
	}, [ item, onClose ] );

	if ( ! item ) {
		return null;
	}

	const portalTarget =
		document.getElementById( 'joc-blockora-morph-app' ) || document.body;

	return createPortal(
		<div
			className="bm-modal-root bm-fade fixed inset-0 z-[100000] flex items-center justify-center p-4 font-sans sm:p-6"
			role="dialog"
			aria-modal="true"
			aria-labelledby="bm-shortcode-preview-title"
		>
			<div className="bm-modal-backdrop" onClick={ onClose } />
			<div className="bm-rise relative z-10 flex h-[min(900px,94vh)] w-full max-w-[1600px] flex-col overflow-hidden rounded-xl border border-outline-variant/30 bg-surface-lowest shadow-card">
				<header className="flex shrink-0 items-start justify-between gap-4 border-b border-outline-variant/20 px-5 py-4">
					<div className="min-w-0">
						<p className="mb-1 text-caps-micro font-bold uppercase tracking-widest text-outline">
							{ __( 'Preview', 'joc-blockora-morph' ) }
						</p>
						<h2
							id="bm-shortcode-preview-title"
							className="m-0 truncate text-headline-lg text-on-surface"
						>
							{ item.name }
						</h2>
						<p className="mt-1 mb-0 truncate text-body-md text-on-surface-variant">
							<code>{ item.shortcode }</code>
						</p>
					</div>
					<button
						type="button"
						className="bm-btn bm-btn--ghost bm-btn--sm shrink-0"
						onClick={ onClose }
					>
						{ __( 'Close', 'joc-blockora-morph' ) }
					</button>
				</header>
				<PreviewFrame html={ preview } className="min-h-0 flex-1" />
				<footer className="flex shrink-0 flex-wrap items-center justify-end gap-2 border-t border-outline-variant/20 bg-surface-low/60 px-5 py-3">
					<button
						type="button"
						className="bm-btn bm-btn--ghost bm-btn--sm"
						onClick={ onClose }
					>
						{ __( 'Close', 'joc-blockora-morph' ) }
					</button>
					<button
						type="button"
						className="bm-btn bm-btn--primary bm-btn--sm"
						onClick={ onEdit }
					>
						{ __( 'Edit content', 'joc-blockora-morph' ) }
					</button>
				</footer>
			</div>
		</div>,
		portalTarget
	);
}

/**
 * Dedicated list: Shortcode (content) blocks.
 */
export default function ShortcodeBlocks( {
	onNavigate,
	embedded = false,
	sidebarCategory = null,
	onLibraryChange,
} ) {
	const [ editorBlocks, setEditorBlocks ] = useState( [] );
	const [ contentBlocks, setContentBlocks ] = useState( [] );
	const [ loading, setLoading ] = useState( true );
	const [ pickerOpen, setPickerOpen ] = useState( false );
	const [ creating, setCreating ] = useState( false );
	const [ confirmDelete, setConfirmDelete ] = useState( null );
	const [ confirmBusy, setConfirmBusy ] = useState( false );
	const [ copiedId, setCopiedId ] = useState( null );
	const [ previewItem, setPreviewItem ] = useState( null );
	const [ query, setQuery ] = useState( '' );
	const [ category, setCategory ] = useState( 'all' );
	const [ updateTarget, setUpdateTarget ] = useState( null );
	const [ selectedIds, setSelectedIds ] = useState( [] );
	const [ helpOpen, setHelpOpen ] = useState( false );
	const [ confirmAction, setConfirmAction ] = useState( null );

	const sidebarFilter =
		embedded &&
		sidebarCategory &&
		sidebarCategory !== 'all'
			? String( sidebarCategory )
			: null;

	const load = () =>
		Promise.all( [ api.get( '/user-blocks' ), api.get( '/user-contents' ) ] ).then(
			( [ ub, uc ] ) => {
				const contents = uc.items || [];
				setEditorBlocks( ub.items || [] );
				setContentBlocks( contents );
				const available = new Set( contents.map( ( item ) => item.id ) );
				setSelectedIds( ( prev ) =>
					prev.filter( ( id ) => available.has( id ) )
				);
			}
		);

	const reloadLibrary = async () => {
		await load();
		if ( typeof onLibraryChange === 'function' ) {
			onLibraryChange();
		}
	};

	useEffect( () => {
		load().finally( () => setLoading( false ) );
	}, [] );

	const selectableBlocks = useMemo(
		() => editorBlocks.filter( ( b ) => b.isEnabled ),
		[ editorBlocks ]
	);

	const categoryOptions = useMemo(
		() => categoryOptionsFromItems( contentBlocks ),
		[ contentBlocks ]
	);

	const contentWithUpdates = useMemo( () => {
		const parentFlag = new Map(
			( editorBlocks || [] ).map( ( block ) => [
				block.id,
				Boolean( block.updateAvailable ),
			] )
		);
		return ( contentBlocks || [] ).map( ( item ) => ( {
			...item,
			updateAvailable:
				Boolean( item.updateAvailable ) ||
				Boolean( parentFlag.get( item.userBlockId ) ),
		} ) );
	}, [ contentBlocks, editorBlocks ] );

	const effectiveCategory = sidebarFilter || category;

	const filteredContentBlocks = useMemo(
		() =>
			filterBlocksList( contentWithUpdates, {
				query,
				category: effectiveCategory,
				getHaystack: ( item ) =>
					`${ item.name || '' } ${ item.shortcode || '' } ${ item.userBlockName || '' } ${ item.categoryName || '' }`,
			} ),
		[ contentWithUpdates, query, effectiveCategory ]
	);

	/** Unique parent Gutenberg block IDs for selected shortcodes with updates. */
	const selectedParentIds = useMemo( () => {
		const ids = new Set();
		contentWithUpdates.forEach( ( item ) => {
			if (
				selectedIds.includes( item.id ) &&
				item.updateAvailable &&
				item.userBlockId
			) {
				ids.add( item.userBlockId );
			}
		} );
		return Array.from( ids );
	}, [ contentWithUpdates, selectedIds ] );

	const selectedInView = useMemo( () => {
		const viewIds = new Set(
			filteredContentBlocks.map( ( item ) => item.id )
		);
		return selectedIds.filter( ( id ) => viewIds.has( id ) );
	}, [ selectedIds, filteredContentBlocks ] );

	const allFilteredSelected =
		filteredContentBlocks.length > 0 &&
		selectedInView.length === filteredContentBlocks.length;

	const toggleSelected = ( id ) => {
		setSelectedIds( ( prev ) =>
			prev.includes( id )
				? prev.filter( ( x ) => x !== id )
				: [ ...prev, id ]
		);
	};

	const toggleSelectAllFiltered = () => {
		if ( allFilteredSelected ) {
			const drop = new Set(
				filteredContentBlocks.map( ( item ) => item.id )
			);
			setSelectedIds( ( prev ) =>
				prev.filter( ( id ) => ! drop.has( id ) )
			);
			return;
		}
		setSelectedIds( ( prev ) => {
			const next = new Set( prev );
			filteredContentBlocks.forEach( ( item ) => next.add( item.id ) );
			return Array.from( next );
		} );
	};

	const runBulkUpdate = async ( parentIds ) => {
		setConfirmBusy( true );
		try {
			const res = await api.post( '/user-blocks/sync-templates', {
				ids: parentIds,
				all: false,
			} );
			await reloadLibrary();
			setSelectedIds( [] );
			setConfirmAction( null );
			const failed = res?.failed?.length || 0;
			const count = res?.count || 0;
			if ( failed > 0 ) {
				window.alert(
					sprintf(
						/* translators: 1: updated count, 2: failed count */
						__(
							'Updated %1$d block(s); %2$d failed.',
							'joc-blockora-morph'
						),
						count,
						failed
					)
				);
			}
		} catch ( e ) {
			window.alert(
				e?.message ||
					__( 'Could not apply library updates.', 'joc-blockora-morph' )
			);
		}
		setConfirmBusy( false );
	};

	const runBulkDelete = async ( ids ) => {
		setConfirmBusy( true );
		try {
			const results = await Promise.allSettled(
				ids.map( ( id ) => api.del( `/user-contents/${ id }` ) )
			);
			const failed = results.filter( ( r ) => r.status === 'rejected' )
				.length;
			await reloadLibrary();
			setSelectedIds( [] );
			setConfirmAction( null );
			setConfirmDelete( null );
			if ( failed > 0 ) {
				window.alert(
					sprintf(
						/* translators: 1: deleted count, 2: failed count */
						__(
							'Deleted %1$d shortcode(s); %2$d failed.',
							'joc-blockora-morph'
						),
						ids.length - failed,
						failed
					)
				);
			}
		} catch ( e ) {
			window.alert(
				e?.message ||
					__( 'Could not delete shortcodes.', 'joc-blockora-morph' )
			);
		}
		setConfirmBusy( false );
	};

	const createContent = async ( parent ) => {
		if ( ! parent?.id || creating || ! parent.isEnabled ) {
			return;
		}
		setCreating( true );
		try {
			const created = await api.post( '/user-contents', {
				userBlockId: Number( parent.id ),
				name: `${ parent.name || 'Block' } shortcode`,
			} );
			setPickerOpen( false );
			await reloadLibrary();
			onNavigate( `content/${ created.id }` );
		} catch ( e ) {
			window.alert(
				e?.message || __( 'Could not create shortcode block.', 'joc-blockora-morph' )
			);
		}
		setCreating( false );
	};

	const removeContent = async ( id ) => {
		setConfirmBusy( true );
		try {
			await api.del( `/user-contents/${ id }` );
			await reloadLibrary();
			setConfirmDelete( null );
		} catch ( e ) {
			window.alert(
				e?.message || __( 'Could not delete shortcode.', 'joc-blockora-morph' )
			);
		}
		setConfirmBusy( false );
	};

	const copyShortcode = async ( item ) => {
		try {
			await navigator.clipboard.writeText( item.shortcode );
			setCopiedId( item.id );
			window.setTimeout( () => {
				setCopiedId( ( prev ) => ( prev === item.id ? null : prev ) );
			}, 1600 );
		} catch ( e ) {
			window.prompt( __( 'Copy shortcode', 'joc-blockora-morph' ), item.shortcode );
		}
	};

	const hasEditors = editorBlocks.length > 0;
	const canCreate = selectableBlocks.length > 0;

	return (
		<div
			className={
				embedded
					? 'bm-rise w-full font-sans'
					: 'bm-rise mx-auto w-full max-w-canvas px-gutter py-stack-lg font-sans'
			}
		>
			<header className="mb-stack-lg flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
				<div className="space-y-2">
					{ ! embedded ? (
						<button
							type="button"
							className="bm-btn bm-btn--ghost bm-btn--sm"
							onClick={ () => onNavigate( 'my-blocks' ) }
						>
							← { __( 'Dashboard', 'joc-blockora-morph' ) }
						</button>
					) : (
						<p className="m-0 text-caps-micro font-bold uppercase tracking-widest text-outline">
							{ __( 'My Library', 'joc-blockora-morph' ) }
						</p>
					) }
					{ ! embedded ? (
						<p className="m-0 text-caps-micro font-bold uppercase tracking-widest text-outline">
							{ __(
								'For widgets, templates & reuse',
								'joc-blockora-morph'
							) }
						</p>
					) : null }
					<h1 className="m-0 flex items-center gap-2 text-display-sm text-on-background">
						{ ! embedded ? (
							<span
								className="dashicons dashicons-shortcode text-primary"
								aria-hidden="true"
							/>
						) : null }
						{ __( 'Shortcode Blocks', 'joc-blockora-morph' ) }
					</h1>
					<p className="m-0 max-w-2xl text-body-lg text-on-surface-variant">
						{ __(
							'Fill in real content once, then paste a shortcode anywhere — sidebars, classic editor, or page builders.',
							'joc-blockora-morph'
						) }
					</p>
				</div>
				<div className="bm-gb-header-actions flex flex-wrap items-center gap-2.5 shrink-0 self-start sm:self-center">
					<button
						type="button"
						className="bm-btn bm-btn--primary shrink-0"
						onClick={ () => setPickerOpen( true ) }
						disabled={ ! canCreate }
						title={
							canCreate
								? undefined
								: __(
										'Import and enable a Gutenberg block first.',
										'joc-blockora-morph'
								  )
						}
					>
						{ __( 'Create shortcode', 'joc-blockora-morph' ) }
					</button>
				</div>
			</header>

			{ ! loading && hasEditors && contentBlocks.length > 0 ? (
				<BlocksListFilters
					query={ query }
					onQueryChange={ setQuery }
					category={ effectiveCategory }
					onCategoryChange={ setCategory }
					categoryOptions={ categoryOptions }
					hideCategoryFilter={ Boolean( sidebarFilter ) }
					searchPlaceholder={ __(
						'Search shortcodes…',
						'joc-blockora-morph'
					) }
					resultCount={ filteredContentBlocks.length }
					totalCount={
						sidebarFilter
							? contentBlocks.filter( ( b ) =>
									blockMatchesCategory( b, sidebarFilter )
							  ).length
							: contentBlocks.length
					}
					onClear={ () => {
						setQuery( '' );
						if ( ! sidebarFilter ) {
							setCategory( 'all' );
						}
					} }
				/>
			) : null }

			<div className="bm-card bm-dash-section__card">
				{ loading && (
					<div className="p-2">
						<LibrarySkeleton variant="list" count={ 5 } />
					</div>
				) }

				{ ! loading && ! hasEditors && (
					<div className="bm-empty bm-empty--muted">
						<span
							className="bm-empty__icon dashicons dashicons-lock"
							aria-hidden="true"
						/>
						<h3 className="bm-empty__title">
							{ __(
								'Available after you import a Gutenberg block',
								'joc-blockora-morph'
							) }
						</h3>
						<p className="bm-empty__text">
							{ __(
								'Shortcodes are copies of a Gutenberg block with locked-in content. Import and customize a block first, then create a shortcode from it.',
								'joc-blockora-morph'
							) }
						</p>
						<div className="flex flex-wrap gap-2">
							<button
								type="button"
								className="bm-btn bm-btn--primary"
								onClick={ () => onNavigate( 'library' ) }
							>
								{ __( 'Browse JBM Library', 'joc-blockora-morph' ) }
							</button>
							<button
								type="button"
								className="bm-btn bm-btn--ghost"
								onClick={ () => onNavigate( 'gutenberg-blocks' ) }
							>
								{ __( 'View Gutenberg Blocks', 'joc-blockora-morph' ) }
							</button>
						</div>
					</div>
				) }

				{ ! loading && hasEditors && contentBlocks.length === 0 && (
					<div className="bm-empty">
						<span
							className="bm-empty__icon dashicons dashicons-shortcode"
							aria-hidden="true"
						/>
						<h3 className="bm-empty__title">
							{ __( 'Create your first shortcode', 'joc-blockora-morph' ) }
						</h3>
						<p className="bm-empty__text">
							{ canCreate
								? __(
										'Pick a Gutenberg block, lock in real content, and get a pasteable [jocbm_blockora_morph] tag.',
										'joc-blockora-morph'
								  )
								: __(
										'All of your Gutenberg blocks are hidden. Enable one in My Blocks before creating a shortcode.',
										'joc-blockora-morph'
								  ) }
						</p>
						{ canCreate ? (
							<button
								type="button"
								className="bm-btn bm-btn--primary"
								onClick={ () => setPickerOpen( true ) }
							>
								{ __( 'Create shortcode', 'joc-blockora-morph' ) }
							</button>
						) : (
							<button
								type="button"
								className="bm-btn bm-btn--ghost"
								onClick={ () => onNavigate( 'gutenberg-blocks' ) }
							>
								{ __( 'View Gutenberg Blocks', 'joc-blockora-morph' ) }
							</button>
						) }
					</div>
				) }

				{ ! loading && hasEditors && contentBlocks.length > 0 && (
					<>
						<div className="bm-help">
							<div className="bm-help__toolbar">
								<button
									type="button"
									className="bm-help__toggle"
									aria-expanded={ helpOpen }
									onClick={ () =>
										setHelpOpen( ( open ) => ! open )
									}
								>
									{ __(
										'How do I use a shortcode?',
										'joc-blockora-morph'
									) }
								</button>
								{ filteredContentBlocks.length > 0 ? (
									<div className="bm-help__actions">
										<label className="bm-btn bm-btn--soft bm-btn--sm bm-help__select-all">
											<input
												type="checkbox"
												checked={ allFilteredSelected }
												ref={ ( el ) => {
													if ( el ) {
														el.indeterminate =
															selectedInView.length >
																0 &&
															! allFilteredSelected;
													}
												} }
												onChange={
													toggleSelectAllFiltered
												}
											/>
											<span>
												{ allFilteredSelected
													? __(
															'Deselect all',
															'joc-blockora-morph'
													  )
													: __(
															'Select all',
															'joc-blockora-morph'
													  ) }
											</span>
										</label>
										<button
											type="button"
											className="bm-btn bm-btn--primary bm-btn--sm"
											disabled={
												selectedParentIds.length ===
													0 || confirmBusy
											}
											onClick={ () =>
												setConfirmAction( {
													type: 'bulk-update',
													ids: selectedParentIds,
												} )
											}
										>
											{ sprintf(
												/* translators: %d: selected parent block count */
												__(
													'Update (%d)',
													'joc-blockora-morph'
												),
												selectedParentIds.length
											) }
										</button>
										<button
											type="button"
											className="bm-btn bm-btn--danger bm-btn--sm"
											disabled={
												selectedIds.length === 0 ||
												confirmBusy
											}
											onClick={ () =>
												setConfirmAction( {
													type: 'bulk-delete',
													ids: [ ...selectedIds ],
												} )
											}
										>
											{ sprintf(
												/* translators: %d: selected count */
												__(
													'Delete (%d)',
													'joc-blockora-morph'
												),
												selectedIds.length
											) }
										</button>
									</div>
								) : null }
							</div>
							{ helpOpen ? (
								<ol className="bm-help__list">
									<li>
										{ __(
											'Edit the shortcode to fill in real text and images.',
											'joc-blockora-morph'
										) }
									</li>
									<li>
										{ __(
											'Copy the [jocbm_blockora_morph] tag from the list.',
											'joc-blockora-morph'
										) }
									</li>
									<li>
										{ __(
											'Paste it in a widget, classic editor, or page builder.',
											'joc-blockora-morph'
										) }
									</li>
								</ol>
							) : null }
						</div>

						{ filteredContentBlocks.length === 0 ? (
							<p className="bm-list-filters__empty">
								{ __(
									'No shortcodes match your search or category.',
									'joc-blockora-morph'
								) }
							</p>
						) : (
						<>
						<div className="bm-gb-list bm-gb-list--selectable">
							{ filteredContentBlocks.map( ( item ) => (
								<article
									key={ item.id }
									className={
										'bm-gb-card' +
										( item.updateAvailable
											? ' has-update'
											: '' )
									}
								>
									<label className="bm-gb-card__check">
										<input
											type="checkbox"
											checked={ selectedIds.includes(
												item.id
											) }
											onChange={ () =>
												toggleSelected( item.id )
											}
											aria-label={ sprintf(
												/* translators: %s: shortcode name */
												__(
													'Select %s',
													'joc-blockora-morph'
												),
												item.name
											) }
										/>
									</label>
									<button
										type="button"
										className="bm-gb-card__thumb"
										onClick={ () => setPreviewItem( item ) }
										aria-label={ __(
											'Preview saved shortcode',
											'joc-blockora-morph'
										) }
									>
										<LibraryCardThumb
											block={ shortcodePreviewBlock( item ) }
										/>
									</button>

									<div className="bm-gb-card__content">
										<div className="bm-gb-card__main">
											<div className="bm-gb-card__body min-w-0">
												<div className="bm-gb-card__title-row">
													<strong className="bm-gb-card__title">
														{ item.name }
													</strong>
													<span className="bm-gb-card__badges">
														{ item.isEnabled ? (
															<span className="bm-badge bm-badge--active">
																{ __( 'Active', 'joc-blockora-morph' ) }
															</span>
														) : (
															<span className="bm-badge bm-badge--disabled">
																{ __( 'Hidden', 'joc-blockora-morph' ) }
															</span>
														) }
														{ item.updateAvailable ? (
															<span className="bm-badge bm-badge--update">
																{ __(
																	'Update available',
																	'joc-blockora-morph'
																) }
															</span>
														) : null }
													</span>
												</div>
												<p className="bm-gb-card__meta">
													<code className="bm-gb-card__code">
														{ item.shortcode }
													</code>
													<span className="bm-gb-card__meta-sep">
														·
													</span>
													{ item.categoryName
														? `${ item.categoryName } · `
														: '' }
													{ __( 'from', 'joc-blockora-morph' ) }{ ' ' }
													{ item.userBlockName }
												</p>
											</div>
											{ item.updateAvailable ? (
												<button
													type="button"
													className="bm-btn bm-btn--primary bm-btn--sm bm-gb-card__update"
													onClick={ () =>
														setUpdateTarget( {
															id: item.userBlockId,
															name:
																item.userBlockName ||
																item.name,
														} )
													}
												>
													{ __(
														'Review update',
														'joc-blockora-morph'
													) }
												</button>
											) : null }
										</div>

										<div className="bm-gb-card__actions">
											<button
												type="button"
												className="bm-btn bm-btn--ghost bm-btn--sm"
												onClick={ () => setPreviewItem( item ) }
											>
												<span
													className="dashicons dashicons-visibility"
													aria-hidden="true"
												/>
												{ __( 'Preview', 'joc-blockora-morph' ) }
											</button>
											<button
												type="button"
												className="bm-btn bm-btn--ghost bm-btn--sm"
												onClick={ () => copyShortcode( item ) }
											>
												<span
													className={
														copiedId === item.id
															? 'dashicons dashicons-yes'
															: 'dashicons dashicons-clipboard'
													}
													aria-hidden="true"
												/>
												{ copiedId === item.id
													? __( 'Copied', 'joc-blockora-morph' )
													: __( 'Copy shortcode', 'joc-blockora-morph' ) }
											</button>
											<button
												type="button"
												className="bm-gb-card__icon-btn"
												onClick={ () =>
													onNavigate( `content/${ item.id }` )
												}
												aria-label={ __(
													'Edit content',
													'joc-blockora-morph'
												) }
												title={ __( 'Edit content', 'joc-blockora-morph' ) }
											>
												<span
													className="dashicons dashicons-edit"
													aria-hidden="true"
												/>
											</button>
											<button
												type="button"
												className="bm-gb-card__icon-btn is-danger"
												onClick={ () => setConfirmDelete( item ) }
												aria-label={ __(
													'Delete shortcode',
													'joc-blockora-morph'
												) }
												title={ __( 'Delete', 'joc-blockora-morph' ) }
											>
												<span
													className="dashicons dashicons-trash"
													aria-hidden="true"
												/>
											</button>
										</div>
									</div>
								</article>
							) ) }
						</div>
						</>
						) }
					</>
				) }
			</div>

			{ pickerOpen && canCreate && (
				<PickUserBlockModal
					blocks={ selectableBlocks }
					creating={ creating }
					onClose={ () => {
						if ( ! creating ) {
							setPickerOpen( false );
						}
					} }
					onSelect={ createContent }
				/>
			) }

			{ updateTarget ? (
				<TemplateUpdateModal
					blockId={ updateTarget.id }
					blockName={ updateTarget.name }
					onClose={ () => setUpdateTarget( null ) }
					onApplied={ () => {
						setSelectedIds( [] );
						reloadLibrary();
					} }
				/>
			) : null }

			{ previewItem ? (
				<ShortcodePreviewModal
					item={ previewItem }
					onClose={ () => setPreviewItem( null ) }
					onEdit={ () => {
						const id = previewItem.id;
						setPreviewItem( null );
						onNavigate( `content/${ id }` );
					} }
				/>
			) : null }

			<ConfirmModal
				open={ confirmAction?.type === 'bulk-update' }
				title={ __( 'Apply library updates?', 'joc-blockora-morph' ) }
				description={ __(
					'This updates the parent Gutenberg block template for the selected shortcodes. Your shortcode content is kept; new fields get defaults from the library.',
					'joc-blockora-morph'
				) }
				icon="update"
				tone="warning"
				busy={ confirmBusy }
				confirmLabel={ sprintf(
					/* translators: %d: number of parent blocks */
					__( 'Update selected (%d)', 'joc-blockora-morph' ),
					confirmAction?.ids?.length || 0
				) }
				onConfirm={ () => {
					if ( Array.isArray( confirmAction?.ids ) ) {
						runBulkUpdate( confirmAction.ids );
					}
				} }
				onCancel={ () => {
					if ( ! confirmBusy ) {
						setConfirmAction( null );
					}
				} }
			>
				{ confirmAction?.type === 'bulk-update' ? (
					<BulkConfirmItemsList
						label={ __( 'Parent blocks', 'joc-blockora-morph' ) }
						items={ ( confirmAction.ids || [] ).map( ( id ) => {
							const block = editorBlocks.find(
								( row ) => row.id === id
							);
							const shortcodes = contentWithUpdates.filter(
								( row ) =>
									selectedIds.includes( row.id ) &&
									row.userBlockId === id
							);
							const metaParts = [
								block?.systemName,
								block?.variant,
								shortcodes.length
									? sprintf(
											/* translators: %d: shortcode count */
											__(
												'%d shortcode(s)',
												'joc-blockora-morph'
											),
											shortcodes.length
									  )
									: null,
							].filter( Boolean );
							return {
								id,
								title: block?.name || `#${ id }`,
								meta: metaParts.join( ' · ' ),
							};
						} ) }
					/>
				) : null }
			</ConfirmModal>

			<ConfirmModal
				open={ confirmAction?.type === 'bulk-delete' }
				title={ __( 'Delete selected shortcodes?', 'joc-blockora-morph' ) }
				description={ __(
					'This permanently removes the selected shortcodes. Any pasted tags using them will stop rendering.',
					'joc-blockora-morph'
				) }
				icon="trash"
				tone="danger"
				busy={ confirmBusy }
				confirmLabel={ sprintf(
					/* translators: %d: number of shortcodes */
					__( 'Delete selected (%d)', 'joc-blockora-morph' ),
					confirmAction?.ids?.length || 0
				) }
				onConfirm={ () => {
					if ( Array.isArray( confirmAction?.ids ) ) {
						runBulkDelete( confirmAction.ids );
					}
				} }
				onCancel={ () => {
					if ( ! confirmBusy ) {
						setConfirmAction( null );
					}
				} }
			>
				{ confirmAction?.type === 'bulk-delete' ? (
					<BulkConfirmItemsList
						label={ __( 'Shortcodes', 'joc-blockora-morph' ) }
						items={ ( confirmAction.ids || [] ).map( ( id ) => {
							const item = contentBlocks.find(
								( row ) => row.id === id
							);
							return {
								id,
								title: item?.name || `#${ id }`,
								meta: [
									item?.shortcode,
									item?.userBlockName,
								]
									.filter( Boolean )
									.join( ' · ' ),
							};
						} ) }
					/>
				) : null }
			</ConfirmModal>

			<ConfirmModal
				open={ !! confirmDelete }
				title={ __( 'Delete this shortcode?', 'joc-blockora-morph' ) }
				description={ __(
					'This permanently removes the shortcode. Any pasted tags using it will stop rendering.',
					'joc-blockora-morph'
				) }
				icon="trash"
				tone="danger"
				busy={ confirmBusy }
				confirmLabel={ __( 'Delete shortcode', 'joc-blockora-morph' ) }
				onConfirm={ () => {
					if ( confirmDelete?.id ) {
						removeContent( confirmDelete.id );
					}
				} }
				onCancel={ () => {
					if ( ! confirmBusy ) {
						setConfirmDelete( null );
					}
				} }
			>
				{ confirmDelete ? (
					<div className="bm-confirm-modal__meta">
						<div className="bm-confirm-modal__meta-row">
							<span className="bm-confirm-modal__meta-label">
								{ __( 'Name', 'joc-blockora-morph' ) }
							</span>
							<span className="bm-confirm-modal__meta-value">
								{ confirmDelete.name }
							</span>
						</div>
						<div className="bm-confirm-modal__meta-row">
							<span className="bm-confirm-modal__meta-label">
								{ __( 'Shortcode', 'joc-blockora-morph' ) }
							</span>
							<span className="bm-confirm-modal__meta-value">
								<code>{ confirmDelete.shortcode }</code>
							</span>
						</div>
						{ confirmDelete.userBlockName ? (
							<div className="bm-confirm-modal__meta-row">
								<span className="bm-confirm-modal__meta-label">
									{ __( 'From', 'joc-blockora-morph' ) }
								</span>
								<span className="bm-confirm-modal__meta-value">
									{ confirmDelete.userBlockName }
								</span>
							</div>
						) : null }
					</div>
				) : null }
			</ConfirmModal>
		</div>
	);
}
