import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import {
	Modal,
	Button,
	Flex,
	Notice,
	privateApis as componentsPrivateApis,
	__experimentalVStack as VStack,
	__experimentalHStack as HStack,
} from '@wordpress/components';
import {
	PlainText,
	store as blockEditorStore,
	privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';
import { fullscreen, square } from '@wordpress/icons';
import { useViewportMatch } from '@wordpress/compose';
import { unlock } from '../lock-unlock';
import Preview from './preview';
import { parseContent, serializeContent } from './utils';

const { Tabs } = unlock( componentsPrivateApis );
const { useNativeUndo } = unlock( blockEditorPrivateApis );

export default function HTMLEditModal( { onRequestClose, content, onUpdate } ) {
	// Parse content into separate sections and use as initial state
	const { html, css, js } = parseContent( content );
	const [ editedHtml, setEditedHtml ] = useState( html );
	const [ editedCss, setEditedCss ] = useState( css );
	const [ editedJs, setEditedJs ] = useState( js );
	const [ isFullscreen, setIsFullscreen ] = useState( false );
	// The fields hold local state that is only committed when the dialog is
	// saved, so undo and redo within them must remain the browser's own.
	const nativeUndoRef = useNativeUndo();

	const isMobileViewport = useViewportMatch( 'small', '<' );

	// Check if user has permission to save scripts and get editor styles
	const { canUserUseUnfilteredHTML } = useSelect( ( select ) => {
		const settings = select( blockEditorStore ).getSettings();
		return {
			canUserUseUnfilteredHTML:
				settings.__experimentalCanUserUseUnfilteredHTML,
		};
	}, [] );

	// Determine if we should show a warning about CSS/JS content being stripped
	const hasRestrictedContent =
		! canUserUseUnfilteredHTML && ( css.trim() || js.trim() );

	const handleUpdate = () => {
		// For users without unfiltered_html capability, strip CSS and JS content
		// to prevent kses from leaving broken content
		onUpdate(
			serializeContent( {
				html: editedHtml,
				css: canUserUseUnfilteredHTML ? editedCss : '',
				js: canUserUseUnfilteredHTML ? editedJs : '',
			} )
		);
	};
	const handleUpdateAndClose = () => {
		handleUpdate();
		onRequestClose();
	};
	const toggleFullscreen = () => {
		setIsFullscreen( ( prevState ) => ! prevState );
	};

	return (
		<>
			<Modal
				title={ __( 'Edit HTML' ) }
				onRequestClose={ onRequestClose }
				className="block-library-html__modal"
				size="large"
				isDismissible={ false }
				shouldCloseOnClickOutside={ false }
				isFullScreen={ isFullscreen }
				__experimentalHideHeader
			>
				<Tabs orientation="horizontal" defaultTabId="html">
					<VStack expanded>
						<HStack
							justify="space-between"
							className="block-library-html__modal-header"
						>
							<div>
								<Tabs.TabList>
									<Tabs.Tab tabId="html">HTML</Tabs.Tab>
									{ canUserUseUnfilteredHTML && (
										<Tabs.Tab tabId="css">CSS</Tabs.Tab>
									) }
									{ canUserUseUnfilteredHTML && (
										<Tabs.Tab tabId="js">
											{ __( 'JavaScript' ) }
										</Tabs.Tab>
									) }
								</Tabs.TabList>
							</div>
							{ ! isMobileViewport && (
								<div>
									<Button
										__next40pxDefaultSize
										icon={
											isFullscreen ? square : fullscreen
										}
										label={ __(
											'Enable/disable fullscreen'
										) }
										onClick={ toggleFullscreen }
										variant="tertiary"
									/>
								</div>
							) }
						</HStack>
						{ hasRestrictedContent && (
							<Notice
								status="warning"
								isDismissible={ false }
								className="block-library-html__modal-notice"
							>
								{ __(
									'This block contains CSS or JavaScript that will be removed when you save because you do not have permission to use unfiltered HTML.'
								) }
							</Notice>
						) }
						<Flex
							direction={ isMobileViewport ? 'column' : 'row' }
							className="block-library-html__modal-tabs"
							align="stretch"
							gap={ 8 }
						>
							<div
								ref={ nativeUndoRef }
								className="block-library-html__modal-content"
							>
								<Tabs.TabPanel
									tabId="html"
									focusable={ false }
									className="block-library-html__modal-tab"
								>
									<PlainText
										value={ editedHtml }
										onChange={ setEditedHtml }
										placeholder={ __( 'Write HTML…' ) }
										aria-label={ __( 'HTML' ) }
										className="block-library-html__modal-editor"
										async
									/>
								</Tabs.TabPanel>
								{ canUserUseUnfilteredHTML && (
									<Tabs.TabPanel
										tabId="css"
										focusable={ false }
										className="block-library-html__modal-tab"
									>
										<PlainText
											value={ editedCss }
											onChange={ setEditedCss }
											placeholder={ __( 'Write CSS…' ) }
											aria-label={ __( 'CSS' ) }
											className="block-library-html__modal-editor"
											async
										/>
									</Tabs.TabPanel>
								) }
								{ canUserUseUnfilteredHTML && (
									<Tabs.TabPanel
										tabId="js"
										focusable={ false }
										className="block-library-html__modal-tab"
									>
										<PlainText
											value={ editedJs }
											onChange={ setEditedJs }
											placeholder={ __(
												'Write JavaScript…'
											) }
											aria-label={ __( 'JavaScript' ) }
											className="block-library-html__modal-editor"
											async
										/>
									</Tabs.TabPanel>
								) }
							</div>
							<div className="block-library-html__preview">
								<Preview
									content={ serializeContent( {
										html: editedHtml,
										css: editedCss,
										js: editedJs,
									} ) }
								/>
							</div>
						</Flex>
						<HStack
							alignment="center"
							justify="flex-end"
							spacing={ 4 }
							className="block-library-html__modal-footer"
						>
							<Button
								__next40pxDefaultSize
								variant="tertiary"
								onClick={ onRequestClose }
							>
								{ __( 'Cancel' ) }
							</Button>
							<Button
								__next40pxDefaultSize
								variant="primary"
								onClick={ handleUpdateAndClose }
							>
								{ __( 'Update' ) }
							</Button>
						</HStack>
					</VStack>
				</Tabs>
			</Modal>
		</>
	);
}
