import { useCallback, useEffect, useRef, useState } from '@wordpress/element';

export interface ConfirmAttributeConfig {
	title: string;
	body: string;
	confirmLabel: string;
	cancelLabel?: string;
	details?: string[];
	checkboxLabel?: string;
	typedValue?: string;
	typedLabel?: string;
	timezone?: string;
}

interface PendingConfirmation extends Required< ConfirmAttributeConfig > {
	form: HTMLFormElement;
	submitter: HTMLElement | null;
	opener: HTMLElement | null;
}

interface ConfirmableSubmitEvent extends Event {
	submitter?: HTMLElement | null;
}

export const confirmAttributes = (
	config: ConfirmAttributeConfig
): Record< string, string > => {
	const attributes: Record< string, string > = {
		'data-scds-confirm-title': config.title,
		'data-scds-confirm-body': config.body,
		'data-scds-confirm-confirm-label': config.confirmLabel,
		'data-scds-confirm-cancel-label': config.cancelLabel || 'Back',
	};
	const details = ( config.details || [] ).filter( Boolean );

	if ( details.length > 0 ) {
		attributes[ 'data-scds-confirm-details' ] = details.join( '||' );
	}

	if ( config.checkboxLabel ) {
		attributes[ 'data-scds-confirm-checkbox-label' ] = config.checkboxLabel;
	}

	if ( config.typedValue ) {
		attributes[ 'data-scds-confirm-typed-value' ] = config.typedValue;
		attributes[ 'data-scds-confirm-typed-label' ] =
			config.typedLabel || `Type ${ config.typedValue } to confirm.`;
	}

	if ( config.timezone ) {
		attributes[ 'data-scds-confirm-timezone' ] = config.timezone;
	}

	return attributes;
};

const splitDetails = ( value: string ): string[] =>
	value
		.split( '||' )
		.map( ( item ) => item.trim() )
		.filter( Boolean );

const fieldValue = ( form: HTMLFormElement, name: string ): string => {
	const field = form.elements.namedItem( name );

	return field instanceof HTMLInputElement ||
		field instanceof HTMLSelectElement ||
		field instanceof HTMLTextAreaElement
		? field.value
		: '';
};

const scheduleDetails = (
	form: HTMLFormElement,
	source: HTMLElement
): string[] => {
	const date =
		fieldValue( form, 'schedule_date' ) ||
		fieldValue( form, 'go_live_date' );
	const time =
		fieldValue( form, 'schedule_time' ) ||
		fieldValue( form, 'go_live_time' );
	const timezone = source.dataset.scdsConfirmTimezone || '';

	if ( ! date && ! time && ! timezone ) {
		return [];
	}

	return [
		`Go-live time: ${ [ date, time, timezone ]
			.filter( Boolean )
			.join( ' ' ) }`,
	];
};

const confirmationFromSource = (
	form: HTMLFormElement,
	source: HTMLElement,
	submitter: HTMLElement | null
): PendingConfirmation => ( {
	form,
	submitter,
	opener:
		source.ownerDocument.activeElement instanceof HTMLElement
			? source.ownerDocument.activeElement
			: null,
	title: source.dataset.scdsConfirmTitle || 'Confirm this action?',
	body:
		source.dataset.scdsConfirmBody ||
		'Review what will happen before continuing.',
	confirmLabel: source.dataset.scdsConfirmConfirmLabel || 'Continue',
	cancelLabel: source.dataset.scdsConfirmCancelLabel || 'Back',
	details: [
		...splitDetails( source.dataset.scdsConfirmDetails || '' ),
		...scheduleDetails( form, source ),
	],
	checkboxLabel: source.dataset.scdsConfirmCheckboxLabel || '',
	typedValue: source.dataset.scdsConfirmTypedValue || '',
	typedLabel: source.dataset.scdsConfirmTypedLabel || '',
	timezone: source.dataset.scdsConfirmTimezone || '',
} );

const confirmationSource = (
	form: HTMLFormElement,
	submitter: HTMLElement | null
): HTMLElement | null => {
	const selector = '[data-scds-confirm-title]';

	if ( submitter ) {
		const source = submitter.closest( selector );

		if ( source instanceof HTMLElement ) {
			return source;
		}
	}

	return form.matches( selector ) ? form : null;
};

export const useConfirmableForms = ( rootRef: {
	current: HTMLElement | null;
} ) => {
	const [ confirmation, setConfirmation ] =
		useState< PendingConfirmation | null >( null );
	const confirmedForms = useRef< WeakSet< HTMLFormElement > >(
		new WeakSet()
	);

	useEffect( () => {
		const root = rootRef.current;

		if ( ! root ) {
			return;
		}

		const handleSubmit = ( event: Event ) => {
			const form = event.target;

			if ( ! ( form instanceof HTMLFormElement ) ) {
				return;
			}

			if ( confirmedForms.current.has( form ) ) {
				confirmedForms.current.delete( form );
				return;
			}

			const submitter = ( event as ConfirmableSubmitEvent ).submitter;
			const source = confirmationSource( form, submitter || null );

			if ( ! source ) {
				return;
			}

			event.preventDefault();
			setConfirmation(
				confirmationFromSource( form, source, submitter || null )
			);
		};

		root.addEventListener( 'submit', handleSubmit, true );

		return () => root.removeEventListener( 'submit', handleSubmit, true );
	}, [ rootRef ] );

	const closeConfirmation = useCallback( () => {
		const opener = confirmation?.opener;

		setConfirmation( null );
		window.setTimeout( () => opener?.focus(), 0 );
	}, [ confirmation ] );

	const confirmAction = useCallback( () => {
		if ( ! confirmation ) {
			return;
		}

		const { form, submitter } = confirmation;
		confirmedForms.current.add( form );
		setConfirmation( null );

		window.setTimeout( () => {
			if (
				submitter instanceof HTMLButtonElement ||
				submitter instanceof HTMLInputElement
			) {
				form.requestSubmit( submitter );
				return;
			}

			form.requestSubmit();
		}, 0 );
	}, [ confirmation ] );

	return {
		confirmation,
		closeConfirmation,
		confirmAction,
	};
};

export const ConfirmationDialog = ( {
	confirmation,
	onCancel,
	onConfirm,
}: {
	confirmation: PendingConfirmation | null;
	onCancel: () => void;
	onConfirm: () => void;
} ) => {
	const dialogRef = useRef< HTMLDivElement | null >( null );
	const cancelRef = useRef< HTMLButtonElement | null >( null );
	const [ checked, setChecked ] = useState( false );
	const [ typed, setTyped ] = useState( '' );

	useEffect( () => {
		setChecked( false );
		setTyped( '' );
		window.setTimeout( () => cancelRef.current?.focus(), 0 );
	}, [ confirmation ] );

	useEffect( () => {
		if ( ! confirmation ) {
			return;
		}

		const handleKeyDown = ( event: KeyboardEvent ) => {
			if ( 'Escape' === event.key ) {
				event.preventDefault();
				onCancel();
				return;
			}

			if ( 'Tab' !== event.key || ! dialogRef.current ) {
				return;
			}

			const focusable = Array.from(
				dialogRef.current.querySelectorAll< HTMLElement >(
					'button, input, select, textarea, a[href], [tabindex]:not([tabindex="-1"])'
				)
			).filter( ( item ) => ! item.hasAttribute( 'disabled' ) );

			if ( 0 === focusable.length ) {
				return;
			}

			const first = focusable[ 0 ];
			const last = focusable[ focusable.length - 1 ];

			if (
				event.shiftKey &&
				dialogRef.current.ownerDocument.activeElement === first
			) {
				event.preventDefault();
				last.focus();
				return;
			}

			if (
				! event.shiftKey &&
				dialogRef.current.ownerDocument.activeElement === last
			) {
				event.preventDefault();
				first.focus();
			}
		};

		document.addEventListener( 'keydown', handleKeyDown );

		return () => document.removeEventListener( 'keydown', handleKeyDown );
	}, [ confirmation, onCancel ] );

	if ( ! confirmation ) {
		return null;
	}

	const requiresCheckbox = Boolean( confirmation.checkboxLabel );
	const requiresTyped = Boolean( confirmation.typedValue );
	const typedMatches =
		! requiresTyped ||
		typed.trim().toUpperCase() === confirmation.typedValue.toUpperCase();
	const canConfirm = ( ! requiresCheckbox || checked ) && typedMatches;

	return (
		<div className="scds-modal" role="presentation">
			<div
				className="scds-modal__surface"
				role="dialog"
				aria-modal="true"
				aria-labelledby="scds-confirmation-title"
				aria-describedby="scds-confirmation-body"
				ref={ dialogRef }
			>
				<h2 id="scds-confirmation-title">{ confirmation.title }</h2>
				<p id="scds-confirmation-body">{ confirmation.body }</p>
				{ confirmation.details.length > 0 && (
					<ul className="scds-confirmation-list">
						{ confirmation.details.map( ( detail ) => (
							<li key={ detail }>{ detail }</li>
						) ) }
					</ul>
				) }
				{ requiresCheckbox && (
					<label
						className="scds-choice scds-confirmation-choice"
						htmlFor="scds-confirmation-checkbox"
					>
						<input
							id="scds-confirmation-checkbox"
							type="checkbox"
							checked={ checked }
							onChange={ ( event ) =>
								setChecked( event.target.checked )
							}
						/>
						<span>{ confirmation.checkboxLabel }</span>
					</label>
				) }
				{ requiresTyped && (
					<label
						className="scds-field scds-confirmation-typed"
						htmlFor="scds-confirmation-typed"
					>
						<span>{ confirmation.typedLabel }</span>
						<input
							id="scds-confirmation-typed"
							type="text"
							value={ typed }
							onChange={ ( event ) =>
								setTyped( event.target.value )
							}
						/>
					</label>
				) }
				<div className="scds-button-row">
					<button
						className="scds-button"
						type="button"
						onClick={ onCancel }
						ref={ cancelRef }
					>
						{ confirmation.cancelLabel }
					</button>
					<button
						className="scds-button scds-button--primary"
						type="button"
						onClick={ onConfirm }
						disabled={ ! canConfirm }
					>
						{ confirmation.confirmLabel }
					</button>
				</div>
			</div>
		</div>
	);
};
