/**
 * WordPress dependencies
 */
import domReady from '@safe-wordpress/dom-ready';
import { useInstanceId } from '@safe-wordpress/compose';
import { useDispatch, useSelect } from '@safe-wordpress/data';
import { useRef, useState } from '@safe-wordpress/element';
import { _x } from '@safe-wordpress/i18n';
import { addQueryArgs } from '@safe-wordpress/url';

/**
 * External dependencies
 */
import clsx from 'clsx';
import { store as NAB_DATA } from '@nab/data';
import { store as NAB_EDITOR } from '@nab/editor';
import { setValue, getValue, clearValue } from '@nab/utils';
import type { AlternativeId } from '@nab/types';

/**
 * Internal dependencies
 */
import './style.scss';
import { useEditorState, useMarkAsSavingFunction } from '../../../hooks';
import { Sidebar } from '../sidebar';
import { JavaScriptPreview } from '../preview';

export type LayoutProps = {
	readonly alternativeId: AlternativeId;
};

const MIN_SIDEBAR_WIDTH = 320;
const SETTING_NAME = 'javascript-editor-sidebar-width';

const STYLE = document.createElement( 'style' );
function setWidth( width: number ) {
	width = ! isNaN( width ) ? width : MIN_SIDEBAR_WIDTH;
	width = Math.min( width, window.innerWidth - MIN_SIDEBAR_WIDTH );
	width = Math.max( width, MIN_SIDEBAR_WIDTH );
	STYLE.textContent = `:root{--nab-javascript-editor-sidebar-width: ${ width }px;}`;
	setValue( SETTING_NAME, width );
}

domReady( () => {
	document.head.appendChild( STYLE );
	setWidth( Number.parseInt( getValue( SETTING_NAME, '' ) ) );
} );

export const Layout = ( { alternativeId }: LayoutProps ): JSX.Element => {
	const instanceId = useInstanceId( Layout );
	const iframeId = `nab-javascript-previewer__iframe-${ instanceId }`;
	const [ state ] = useEditorState();
	const javascriptValue = useJavaScriptValue( alternativeId );
	const previewUrl = usePreviewUrl( alternativeId );
	const [ isSaving, save ] = useSave( iframeId );

	const dividerRef = useRef( null );
	const [ isDragging, setIsDragging ] = useState( false );

	return (
		<div
			className={ clsx( {
				'nab-javascript-editor': true,
				'nab-javascript-editor--is-dragging': isDragging,
			} ) }
		>
			<Sidebar
				className={ clsx( {
					'nab-javascript-editor__sidebar': true,
					'nab-javascript-editor__sidebar--is-collapsed':
						! state.areControlsVisible,
				} ) }
				alternativeId={ alternativeId }
				isSaving={ isSaving }
				save={ save }
			/>

			<Divider
				dividerRef={ dividerRef }
				onDragStart={ () => setIsDragging( true ) }
				onDrag={ setWidth }
				onDragEnd={ () => setIsDragging( false ) }
				onDoubleClick={ () => {
					setIsDragging( true );
					setTimeout( () => {
						STYLE.textContent = '';
						setTimeout( () => setIsDragging( false ), 200 );
						clearValue( SETTING_NAME );
					}, 0 );
				} }
			/>

			<JavaScriptPreview
				key="nab-javascript-editor__preview"
				className={ clsx( {
					'nab-javascript-editor__preview': true,
					'nab-javascript-editor__preview--is-fullscreen':
						! state.areControlsVisible,
				} ) }
				iframeId={ iframeId }
				isSaving={ isSaving }
				previewUrl={ previewUrl }
				value={ javascriptValue }
			/>
		</div>
	);
};

type DividerProps = {
	readonly dividerRef: React.MutableRefObject< null >;
	readonly onDragStart?: () => void;
	readonly onDrag?: ( x: number ) => void;
	readonly onDragEnd?: () => void;
	readonly onDoubleClick?: () => void;
};
const Divider = ( {
	dividerRef,
	onDragStart,
	onDrag,
	onDragEnd,
	onDoubleClick,
}: DividerProps ) => {
	const onPointerDown: React.PointerEventHandler< HTMLDivElement > = (
		e
	) => {
		e.currentTarget.setPointerCapture( e.pointerId );
		onDragStart?.();
	};

	const onPointerMove: React.PointerEventHandler< HTMLDivElement > = (
		e
	) => {
		if ( e.currentTarget.hasPointerCapture( e.pointerId ) ) {
			onDrag?.( e.clientX );
		}
	};

	const onPointerUp: React.PointerEventHandler< HTMLDivElement > = ( e ) => {
		if ( e.currentTarget.hasPointerCapture( e.pointerId ) ) {
			e.currentTarget.releasePointerCapture( e.pointerId );
		}
		onDragEnd?.();
	};

	return (
		<div
			ref={ dividerRef }
			className="nab-javascript-editor__resizer"
			role="separator"
			aria-orientation="vertical"
			aria-label={ _x( 'Resize Editor', 'command', 'nelio-ab-testing' ) }
			tabIndex={ 0 }
			onPointerDown={ onPointerDown }
			onPointerMove={ onPointerMove }
			onPointerUp={ onPointerUp }
			onDoubleClick={ onDoubleClick }
		/>
	);
};

// =====
// HOOKS
// =====

const useJavaScriptValue = ( alternativeId: AlternativeId ) =>
	useSelect(
		( select ) =>
			select( NAB_EDITOR ).getAlternative< { code: string } >(
				alternativeId
			)?.attributes?.code || '',
		[ alternativeId ]
	);

const usePreviewUrl = ( alternativeId: AlternativeId ) =>
	useSelect(
		( select ) => {
			const { getAlternative } = select( NAB_EDITOR );
			const alternative = getAlternative( alternativeId );
			if ( ! alternative ) {
				return;
			}

			if ( ! alternative.links.preview ) {
				return;
			}

			return addQueryArgs( alternative.links.preview, {
				'nab-javascript-previewer': true,
			} );
		},
		[ alternativeId ]
	);

const useSave = ( iframeId: string ) => {
	const isSaving = useSelect(
		( select ) =>
			!! select( NAB_DATA ).getPageAttribute(
				'javascript-editor/javascriptEditorState'
			)?.isSaving,
		[]
	);
	const markAsSaving = useMarkAsSavingFunction();

	const { saveExperiment } = useDispatch( NAB_EDITOR );
	const save = () => {
		markAsSaving( true );
		void saveExperiment().then( () => {
			const iframe = document.getElementById(
				iframeId
			) as HTMLIFrameElement;
			iframe?.contentWindow?.location.reload();
			setTimeout( () => markAsSaving( false ), 5000 );
		} );
	};
	return [ isSaving, save ] as const;
};
