/* eslint-env browser */
/* eslint-disable react/prop-types */

/**
 * SocialShareItemsControl — the "Items" element-control for the AAE Social
 * Share.
 *
 * Registered under the type id 'aae-social-share-items' (see ./index.js) and
 * rendered by the editing panel where the PHP side places an
 * AAE_A_Social_Share_Items_Control. Mirrors the Timeline's
 * TimelineItemsControl: a custom list (not Elementor's <Repeater>) whose rows
 * are a LIVE PROJECTION of the social share's real <e-aae-a-social-share-item>
 * children — there is no separate repeater data to keep in sync; the list is
 * read straight off the V1 container model via the useListenTo pattern.
 *
 * Interactions:
 *   - Click a row  → expand it (rename field).
 *   - "+" (Add)    → append a new item; its icon/label children are created
 *                    by Elementor's default_children pipeline.
 *   - Duplicate    → clone that item (styles/ids regenerated by the editor).
 *   - Remove (×)   → delete that item (hidden when only one remains).
 *   - Drag a row   → reorder items (HTML5 native drag).
 */

import * as React from 'react';
import {
	createElements,
	duplicateElements,
	getContainer,
	moveElements,
	removeElements,
	updateElementEditorSettings,
	useElementEditorSettings,
} from '@elementor/editor-elements';
import {
	__privateUseListenTo as useListenTo,
	commandEndEvent,
	v1ReadyEvent,
} from '@elementor/editor-v1-adapters';
import { useElement } from '@elementor/editor-editing-panel';
import {
	Box,
	Collapse,
	IconButton,
	Stack,
	TextField,
	Tooltip,
	Typography,
} from '@elementor/ui';

const ITEM_TYPE = 'e-aae-a-social-share-item';

/**
 * Model for a fresh social-share item. `elements: []` (empty, not undefined)
 * lets Elementor's onElementCreate() populate the default icon/label children
 * via AAE_A_Social_Share_Item::define_default_children().
 */
function buildItemModel( position ) {
	return {
		elType: ITEM_TYPE,
		editor_settings: { title: `Item ${ position }` },
		elements: [],
	};
}

/** Live projection of the social share's direct item children. */
function useSocialShareItems( socialShareId ) {
	const cacheRef = React.useRef( { signature: null, value: [] } );

	return useListenTo(
		[
			v1ReadyEvent(),
			commandEndEvent( 'document/elements/create' ),
			commandEndEvent( 'document/elements/delete' ),
			commandEndEvent( 'document/elements/update' ),
			commandEndEvent( 'document/elements/settings' ),
			commandEndEvent( 'document/elements/set-settings' ),
			commandEndEvent( 'document/elements/duplicate' ),
			commandEndEvent( 'document/elements/move' ),
		],
		() => {
			const children = getContainer( socialShareId )?.model?.get?.( 'elements' );

			if ( ! children ) {
				if ( cacheRef.current.signature !== '' ) {
					cacheRef.current = { signature: '', value: [] };
				}
				return cacheRef.current.value;
			}

			const next = [];
			const signatureParts = [];
			children.each( ( model ) => {
				if ( ( model.get( 'widgetType' ) || model.get( 'elType' ) ) !== ITEM_TYPE ) {
					return;
				}
				const id = model.get( 'id' );
				const editorSettings = model.get( 'editor_settings' ) || {};
				next.push( { id, editorSettings } );
				signatureParts.push( `${ id }:${ editorSettings.title || '' }` );
			} );

			const signature = signatureParts.join( '|' );
			if ( cacheRef.current.signature !== signature ) {
				cacheRef.current = { signature, value: next };
			}
			return cacheRef.current.value;
		},
		[ socialShareId ]
	);
}

export function SocialShareItemsControl( { label } ) {
	const { element } = useElement();
	const socialShareId = element.id;

	const items = useSocialShareItems( socialShareId );

	const rows = ( items || [] ).map( ( item, index ) => ( {
		id: item.id,
		title: item.editorSettings?.title || `Item ${ index + 1 }`,
		index,
	} ) );

	const [ expandedId, setExpandedId ] = React.useState( null );
	const [ dragFrom, setDragFrom ] = React.useState( null );
	const [ dragOver, setDragOver ] = React.useState( null );

	const handleRowClick = ( row ) => {
		setExpandedId( ( cur ) => ( cur === row.id ? null : row.id ) );
	};

	const handleAdd = () => {
		const socialShare = getContainer( socialShareId );
		if ( ! socialShare ) {
			return;
		}
		createElements( {
			title: 'Social Share Item',
			subtitle: 'Item added',
			elements: [
				{
					container: socialShare,
					model: buildItemModel( rows.length + 1 ),
					options: { at: rows.length },
				},
			],
		} );
	};

	const handleDuplicate = ( row ) => {
		duplicateElements( {
			elementIds: [ row.id ],
			title: 'Social Share Item',
			subtitle: 'Item duplicated',
		} );
	};

	const handleRemove = ( row ) => {
		removeElements( {
			elementIds: [ row.id ],
			title: 'Social Share Item',
			subtitle: 'Item removed',
		} );
		if ( expandedId === row.id ) {
			setExpandedId( null );
		}
	};

	const handleDrop = ( toIndex ) => {
		const from = dragFrom;
		setDragFrom( null );
		setDragOver( null );
		if ( from == null || from === toIndex ) {
			return;
		}
		const socialShare = getContainer( socialShareId );
		const movedId = rows[ from ]?.id;
		const movedElement = movedId ? getContainer( movedId ) : null;
		// Guard against a stale index (concurrent create/delete between render
		// and drop): only move if the item is still a child of this social share.
		if ( socialShare && movedElement && movedElement.parent?.id === socialShare.id ) {
			moveElements( {
				title: 'Social Share Item',
				subtitle: 'Item reordered',
				moves: [
					{
						element: movedElement,
						targetContainer: socialShare,
						options: { at: toIndex },
					},
				],
			} );
		}
	};

	return (
		<Stack gap={ 1 }>
			<Stack direction="row" alignItems="center" justifyContent="space-between">
				<Typography variant="caption" sx={ { fontWeight: 500, color: 'text.secondary' } }>
					{ label }
				</Typography>
				<Tooltip title="Add Item">
					<IconButton size="tiny" onClick={ handleAdd } aria-label="Add Item">
						<span style={ { fontSize: 16, lineHeight: 1 } }>+</span>
					</IconButton>
				</Tooltip>
			</Stack>

			<Stack gap={ 0.5 }>
				{ rows.map( ( row ) => {
					const isExpanded = expandedId === row.id;
					const isDragOver = dragOver === row.index && dragFrom !== row.index;
					return (
						<Box
							key={ row.id }
							draggable
							onDragStart={ () => setDragFrom( row.index ) }
							onDragOver={ ( e ) => {
								e.preventDefault();
								setDragOver( row.index );
							} }
							onDrop={ () => handleDrop( row.index ) }
							onDragEnd={ () => {
								setDragFrom( null );
								setDragOver( null );
							} }
							sx={ {
								border: '1px solid',
								borderColor: isDragOver ? 'primary.main' : 'divider',
								borderRadius: 1,
								overflow: 'hidden',
								bgcolor: 'background.default',
							} }
						>
							<Stack
								direction="row"
								alignItems="center"
								gap={ 0.5 }
								onClick={ () => handleRowClick( row ) }
								sx={ {
									px: 1,
									py: 0.75,
									cursor: 'pointer',
									userSelect: 'none',
									'&:hover': { bgcolor: 'action.hover' },
								} }
							>
								<Box
									component="span"
									sx={ { color: 'text.tertiary', cursor: 'grab', fontSize: 14, lineHeight: 1 } }
									aria-hidden
								>
									⠿
								</Box>
								<Typography variant="body2" sx={ { flex: 1, fontWeight: isExpanded ? 600 : 400 } }>
									<RowTitle elementId={ row.id } fallback={ row.title } />
								</Typography>
								<Tooltip title="Duplicate">
									<IconButton
										size="tiny"
										aria-label="Duplicate item"
										onClick={ ( e ) => {
											e.stopPropagation();
											handleDuplicate( row );
										} }
									>
										<span style={ { fontSize: 13, lineHeight: 1 } }>⧉</span>
									</IconButton>
								</Tooltip>
								{ rows.length > 1 && (
									<Tooltip title="Remove">
										<IconButton
											size="tiny"
											aria-label="Remove item"
											onClick={ ( e ) => {
												e.stopPropagation();
												handleRemove( row );
											} }
										>
											<span style={ { fontSize: 14, lineHeight: 1 } }>×</span>
										</IconButton>
									</Tooltip>
								) }
							</Stack>

							<Collapse in={ isExpanded } unmountOnExit>
								<Box sx={ { px: 1.5, py: 1.5, borderTop: '1px solid', borderColor: 'divider' } }>
									<ItemNameField elementId={ row.id } />
								</Box>
							</Collapse>
						</Box>
					);
				} ) }
			</Stack>
		</Stack>
	);
}

/**
 * Row label, read live off the element's own editor_settings rather than off
 * `useSocialShareItems`'s cached projection. The projection only recomputes on
 * a fixed set of document/elements/* commands and a window event that
 * Elementor only dispatches when some OTHER view (e.g. the Navigator row for
 * this element) happens to be listening for the model change — so typing in
 * the rename field below updated the field itself (which already reads this
 * same live hook) but left the row title stuck until something else forced a
 * re-render. Subscribing here directly sidesteps that dependency entirely.
 */
function RowTitle( { elementId, fallback } ) {
	const editorSettings = useElementEditorSettings( elementId );
	return editorSettings?.title || fallback;
}

function ItemNameField( { elementId } ) {
	const editorSettings = useElementEditorSettings( elementId );
	const label = editorSettings?.title ?? '';

	return (
		<Stack gap={ 1 }>
			<Typography variant="caption" sx={ { fontWeight: 500, color: 'text.secondary' } }>
				{ 'Item name' }
			</Typography>
			<TextField
				size="tiny"
				value={ label }
				onChange={ ( { target } ) =>
					updateElementEditorSettings( {
						elementId,
						settings: { title: target.value },
					} )
				}
			/>
		</Stack>
	);
}
