/**
 * Tour editor: live preview, scene strip, hotspots, publish sidebar.
 */
import { useState, useEffect } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { decodeEntities } from '@wordpress/html-entities';
import {
	App,
	Typography,
	Button,
	Input,
	Select,
	Switch,
	Slider,
	Card,
	Tag,
	List,
	Collapse,
	Grid,
	Drawer,
	Spin,
	Space,
	Tooltip,
} from 'antd';
import {
	ArrowLeftOutlined,
	PlusOutlined,
	StarOutlined,
	StarFilled,
	DeleteOutlined,
	EditOutlined,
	PictureOutlined,
	SettingOutlined,
	CopyOutlined,
	EyeOutlined,
} from '@ant-design/icons';

import { config, fetchTour, saveTour, pickImage } from '../../api';
import { navigate } from '../../App';
import LivePreview from './LivePreview';
import HotspotModal from './HotspotModal';

const HOTSPOT_TAG_COLORS = {
	info: 'blue',
	navigation: 'green',
	link: 'purple',
	media: 'gold',
};

const newHotspot = ( yaw = 0, pitch = 0 ) => ( {
	type: 'info',
	title: '',
	content: '',
	yaw,
	pitch,
	target_scene: 0,
	url: '',
	media_id: 0,
	icon: 'default',
} );

const sceneLabel = ( scene, index ) =>
	scene.title ||
	sprintf(
		/* translators: %d: scene number. */
		__( 'Scene %d', 'vistour-virtual-tour-builder' ),
		index + 1
	);

const TourEditor = ( { tourId } ) => {
	const { message } = App.useApp();
	const screens = Grid.useBreakpoint();
	const [ tour, setTour ] = useState( null );
	const [ sceneIndex, setSceneIndex ] = useState( 0 );
	const [ editingHotspot, setEditingHotspot ] = useState( null );
	const [ saving, setSaving ] = useState( false );
	const [ dirty, setDirty ] = useState( false );
	const [ sidebarOpen, setSidebarOpen ] = useState( false );

	useEffect( () => {
		if ( tourId ) {
			fetchTour( tourId )
				.then( ( data ) => {
					const scenes = ( data.meta?._vist360_scenes || [] ).map(
						( scene ) => ( { ...scene } )
					);
					setTour( {
						title: decodeEntities( data.title.raw || '' ),
						status: data.status,
						password: data.password || '',
						link: data.link,
						scenes,
						viewer: {
							...config.viewerDefaults,
							...( data.meta?._vist360_viewer || {} ),
						},
					} );
					// The REST meta stores image IDs only; resolve preview
					// URLs from embedded media on the editor side.
					resolveSceneImages( scenes, setTour );
				} )
				.catch( ( error ) => message.error( error.message ) );
			return;
		}

		let template = null;
		try {
			template = JSON.parse(
				window.sessionStorage.getItem( 'vist360_template' ) || 'null'
			);
		} catch {
			template = null;
		}
		window.sessionStorage.removeItem( 'vist360_template' );

		setTour( {
			title: '',
			status: 'draft',
			password: '',
			link: '',
			scenes: ( template?.scenes || [] ).map( ( scene ) => ( {
				title: scene.title || '',
				type: 'sphere',
				image_id: 0,
				image_url: '',
				hotspots: [],
			} ) ),
			viewer: { ...config.viewerDefaults, ...( template?.viewer || {} ) },
		} );
	}, [ tourId, message ] );

	useEffect( () => {
		if ( ! dirty ) {
			return undefined;
		}
		const warn = ( event ) => {
			event.preventDefault();
			event.returnValue = '';
		};
		window.addEventListener( 'beforeunload', warn );
		return () => window.removeEventListener( 'beforeunload', warn );
	}, [ dirty ] );

	if ( ! tour ) {
		return (
			<div className="vist360-loading">
				<Spin size="large" />
			</div>
		);
	}

	const update = ( changes ) => {
		setTour( ( current ) => ( { ...current, ...changes } ) );
		setDirty( true );
	};

	const updateScene = ( index, changes ) => {
		const scenes = tour.scenes.map( ( scene, i ) =>
			i === index ? { ...scene, ...changes } : scene
		);
		update( { scenes } );
	};

	const updateHotspot = ( hotspotIndex, hotspot ) => {
		const scene = tour.scenes[ sceneIndex ];
		const hotspots = scene.hotspots.map( ( item, i ) =>
			i === hotspotIndex ? hotspot : item
		);
		updateScene( sceneIndex, { hotspots } );
	};

	const addScene = async () => {
		const attachment = await pickImage(
			__(
				'Select a 360° panorama image',
				'vistour-virtual-tour-builder'
			)
		);
		update( {
			scenes: [
				...tour.scenes,
				{
					title: attachment.title || '',
					type: 'sphere',
					image_id: attachment.id,
					image_url: attachment.url,
					hotspots: [],
				},
			],
		} );
		setSceneIndex( tour.scenes.length );
	};

	const handlePick = ( yaw, pitch ) => {
		if ( null !== editingHotspot ) {
			updateHotspot( editingHotspot, {
				...tour.scenes[ sceneIndex ].hotspots[ editingHotspot ],
				yaw,
				pitch,
			} );
			return;
		}
		const scene = tour.scenes[ sceneIndex ];
		updateScene( sceneIndex, {
			hotspots: [ ...scene.hotspots, newHotspot( yaw, pitch ) ],
		} );
		setEditingHotspot( scene.hotspots.length );
	};

	const save = async () => {
		if ( ! tour.title.trim() ) {
			message.error(
				__(
					'Please enter a tour title.',
					'vistour-virtual-tour-builder'
				)
			);
			return;
		}
		setSaving( true );
		try {
			const saved = await saveTour( tourId, {
				title: tour.title,
				status: tour.status,
				password: tour.password,
				meta: {
					_vist360_scenes: tour.scenes.map( ( scene ) => ( {
						title: scene.title,
						type: scene.type,
						image_id: scene.image_id,
						hotspots: scene.hotspots,
					} ) ),
					_vist360_viewer: tour.viewer,
				},
			} );
			setDirty( false );
			message.success(
				__( 'Tour saved.', 'vistour-virtual-tour-builder' )
			);
			if ( ! tourId ) {
				navigate( `edit/${ saved.id }` );
			}
		} catch ( error ) {
			message.error(
				error.message ||
					__(
						'Failed to save tour.',
						'vistour-virtual-tour-builder'
					)
			);
		} finally {
			setSaving( false );
		}
	};

	const scene = tour.scenes[ sceneIndex ] || null;
	const atLimit = tour.scenes.length >= config.maxScenes;

	const sidebar = (
		<Collapse
			defaultActiveKey={ [ 'publish' ] }
			items={ [
				{
					key: 'publish',
					label: __( 'Publish', 'vistour-virtual-tour-builder' ),
					children: (
						<Space direction="vertical" style={ { width: '100%' } }>
							<div className="vist360-label">
								{ __(
									'Status',
									'vistour-virtual-tour-builder'
								) }
								<Select
									style={ { width: '100%' } }
									value={ tour.status }
									onChange={ ( status ) =>
										update( { status } )
									}
									options={ [
										{
											value: 'draft',
											label: __(
												'Draft',
												'vistour-virtual-tour-builder'
											),
										},
										{
											value: 'publish',
											label: __(
												'Published',
												'vistour-virtual-tour-builder'
											),
										},
										{
											value: 'private',
											label: __(
												'Private',
												'vistour-virtual-tour-builder'
											),
										},
									] }
								/>
							</div>
							<div className="vist360-label">
								{ __(
									'Password (optional)',
									'vistour-virtual-tour-builder'
								) }
								<Input
									value={ tour.password }
									onChange={ ( event ) =>
										update( {
											password: event.target.value,
										} )
									}
								/>
							</div>
							{ tourId && (
								<Input
									readOnly
									value={ `[vist360_tour id="${ tourId }"]` }
									addonAfter={
										<CopyOutlined
											onClick={ () =>
												window.navigator.clipboard
													?.writeText(
														`[vist360_tour id="${ tourId }"]`
													)
													.then( () =>
														message.success(
															__(
																'Shortcode copied.',
																'vistour-virtual-tour-builder'
															)
														)
													)
											}
										/>
									}
								/>
							) }
						</Space>
					),
				},
				{
					key: 'viewer',
					label: __(
						'Viewer settings',
						'vistour-virtual-tour-builder'
					),
					children: (
						<Space
							direction="vertical"
							style={ { width: '100%' } }
							size="middle"
						>
							{ [
								[
									'autorotate',
									__(
										'Auto-rotate',
										'vistour-virtual-tour-builder'
									),
								],
								[
									'show_controls',
									__(
										'Show controls',
										'vistour-virtual-tour-builder'
									),
								],
								[
									'show_share',
									__(
										'Share button',
										'vistour-virtual-tour-builder'
									),
								],
								[
									'gyroscope',
									__(
										'Gyroscope',
										'vistour-virtual-tour-builder'
									),
								],
							].map( ( [ key, label ] ) => (
								<div key={ key } className="vist360-toggle-row">
									<span>{ label }</span>
									<Switch
										checked={ !! tour.viewer[ key ] }
										onChange={ ( value ) =>
											update( {
												viewer: {
													...tour.viewer,
													[ key ]: value,
												},
											} )
										}
									/>
								</div>
							) ) }
							<div className="vist360-label">
								{ sprintf(
									/* translators: %d: height in pixels. */
									__(
										'Height: %dpx',
										'vistour-virtual-tour-builder'
									),
									tour.viewer.height || 500
								) }
								<Slider
									min={ 200 }
									max={ 1200 }
									step={ 10 }
									value={ tour.viewer.height || 500 }
									onChange={ ( height ) =>
										update( {
											viewer: {
												...tour.viewer,
												height,
											},
										} )
									}
								/>
							</div>
							<div className="vist360-label">
								{ __(
									'Starting scene',
									'vistour-virtual-tour-builder'
								) }
								<Select
									style={ { width: '100%' } }
									value={ tour.viewer.start_scene || 0 }
									onChange={ ( startScene ) =>
										update( {
											viewer: {
												...tour.viewer,
												start_scene: startScene,
											},
										} )
									}
									options={ tour.scenes.map(
										( item, index ) => ( {
											value: index,
											label: sceneLabel( item, index ),
										} )
									) }
								/>
							</div>
						</Space>
					),
				},
			] }
		/>
	);

	return (
		<div className="vist360-view vist360-editor">
			<div className="vist360-view__header">
				<Space wrap>
					<Button
						icon={ <ArrowLeftOutlined /> }
						type="text"
						onClick={ () => navigate( 'tours' ) }
					>
						{ __( 'Tours', 'vistour-virtual-tour-builder' ) }
					</Button>
					<Input
						className="vist360-editor__title"
						placeholder={ __(
							'Tour title…',
							'vistour-virtual-tour-builder'
						) }
						value={ tour.title }
						onChange={ ( event ) =>
							update( { title: event.target.value } )
						}
					/>
				</Space>
				<Space>
					{ tourId && 'publish' === tour.status && tour.link && (
						<Button
							icon={ <EyeOutlined /> }
							href={ tour.link }
							target="_blank"
						>
							{ screens.sm
								? __(
										'Preview',
										'vistour-virtual-tour-builder'
								  )
								: null }
						</Button>
					) }
					{ ! screens.lg && (
						<Button
							icon={ <SettingOutlined /> }
							onClick={ () => setSidebarOpen( true ) }
							aria-label={ __(
								'Tour settings',
								'vistour-virtual-tour-builder'
							) }
						/>
					) }
					<Button type="primary" loading={ saving } onClick={ save }>
						{ __( 'Save', 'vistour-virtual-tour-builder' ) }
					</Button>
				</Space>
			</div>

			<div className="vist360-editor__layout">
				<div className="vist360-editor__main">
					<LivePreview
						scenes={ tour.scenes }
						sceneIndex={ sceneIndex }
						onPick={ handlePick }
						onHotspotEdit={ setEditingHotspot }
					/>

					<div className="vist360-editor__scenes-header">
						<Typography.Title level={ 5 }>
							{ sprintf(
								/* translators: 1: scene count, 2: max scenes. */
								__(
									'Scenes (%1$d/%2$d)',
									'vistour-virtual-tour-builder'
								),
								tour.scenes.length,
								config.maxScenes
							) }
						</Typography.Title>
					</div>

					<div className="vist360-scene-strip">
						{ tour.scenes.map( ( item, index ) => (
							<button
								key={ index }
								type="button"
								className={ `vist360-scene-thumb ${
									index === sceneIndex ? 'is-active' : ''
								}` }
								onClick={ () => {
									setSceneIndex( index );
									setEditingHotspot( null );
								} }
							>
								{ item.image_url ? (
									<img src={ item.image_url } alt="" />
								) : (
									<PictureOutlined />
								) }
								<span className="vist360-scene-thumb__label">
									{ ( tour.viewer.start_scene || 0 ) ===
										index && <StarFilled /> }
									{ sceneLabel( item, index ) }
								</span>
							</button>
						) ) }
						{ ! atLimit && (
							<button
								type="button"
								className="vist360-scene-thumb vist360-scene-thumb--add"
								onClick={ addScene }
							>
								<PlusOutlined />
								<span className="vist360-scene-thumb__label">
									{ __(
										'Add scene',
										'vistour-virtual-tour-builder'
									) }
								</span>
							</button>
						) }
					</div>

					{ scene && (
						<Card
							size="small"
							title={ __(
								'Selected scene',
								'vistour-virtual-tour-builder'
							) }
							extra={
								<Space>
									<Tooltip
										title={ __(
											'Set as starting scene',
											'vistour-virtual-tour-builder'
										) }
									>
										<Button
											size="small"
											icon={
												( tour.viewer.start_scene ||
													0 ) === sceneIndex ? (
													<StarFilled />
												) : (
													<StarOutlined />
												)
											}
											onClick={ () =>
												update( {
													viewer: {
														...tour.viewer,
														start_scene: sceneIndex,
													},
												} )
											}
										/>
									</Tooltip>
									<Button
										size="small"
										danger
										icon={ <DeleteOutlined /> }
										onClick={ () => {
											update( {
												scenes: tour.scenes.filter(
													( _, i ) => i !== sceneIndex
												),
											} );
											setSceneIndex(
												Math.max( 0, sceneIndex - 1 )
											);
										} }
									/>
								</Space>
							}
						>
							<Space wrap size="middle">
								<Input
									style={ { width: 200 } }
									placeholder={ __(
										'Scene title',
										'vistour-virtual-tour-builder'
									) }
									value={ scene.title }
									onChange={ ( event ) =>
										updateScene( sceneIndex, {
											title: event.target.value,
										} )
									}
								/>
								<Select
									style={ { width: 170 } }
									value={ scene.type }
									onChange={ ( type ) =>
										updateScene( sceneIndex, { type } )
									}
									options={ config.sceneTypes.map(
										( type ) => ( {
											value: type,
											label: type
												.replace( '_', ' ' )
												.replace( /\b\w/g, ( c ) =>
													c.toUpperCase()
												),
										} )
									) }
								/>
								<Button
									onClick={ async () => {
										const attachment = await pickImage(
											__(
												'Select a 360° panorama image',
												'vistour-virtual-tour-builder'
											)
										);
										updateScene( sceneIndex, {
											image_id: attachment.id,
											image_url: attachment.url,
										} );
									} }
								>
									{ scene.image_id
										? __(
												'Change image',
												'vistour-virtual-tour-builder'
										  )
										: __(
												'Select image',
												'vistour-virtual-tour-builder'
										  ) }
								</Button>
							</Space>

							<List
								className="vist360-hotspot-list"
								size="small"
								header={
									<div className="vist360-toggle-row">
										<strong>
											{ sprintf(
												/* translators: %d: hotspot count. */
												__(
													'Hotspots in this scene (%d)',
													'vistour-virtual-tour-builder'
												),
												scene.hotspots.length
											) }
										</strong>
										<Button
											size="small"
											type="link"
											icon={ <PlusOutlined /> }
											onClick={ () => handlePick( 0, 0 ) }
										>
											{ __(
												'Add',
												'vistour-virtual-tour-builder'
											) }
										</Button>
									</div>
								}
								dataSource={ scene.hotspots }
								locale={ {
									emptyText: __(
										'Click the preview to place your first hotspot.',
										'vistour-virtual-tour-builder'
									),
								} }
								renderItem={ ( hotspot, index ) => (
									<List.Item
										actions={ [
											<Button
												key="edit"
												size="small"
												type="text"
												icon={ <EditOutlined /> }
												onClick={ () =>
													setEditingHotspot( index )
												}
											/>,
											<Button
												key="delete"
												size="small"
												type="text"
												danger
												icon={ <DeleteOutlined /> }
												onClick={ () =>
													updateScene( sceneIndex, {
														hotspots:
															scene.hotspots.filter(
																( _, i ) =>
																	i !== index
															),
													} )
												}
											/>,
										] }
									>
										<Tag
											color={
												HOTSPOT_TAG_COLORS[
													hotspot.type
												]
											}
										>
											{ hotspot.type.toUpperCase() }
										</Tag>
										{ hotspot.title ||
											__(
												'(untitled)',
												'vistour-virtual-tour-builder'
											) }
										<Typography.Text type="secondary">
											{ ` · ${ hotspot.yaw }° / ${ hotspot.pitch }°` }
										</Typography.Text>
									</List.Item>
								) }
							/>
						</Card>
					) }
				</div>

				{ screens.lg ? (
					<div className="vist360-editor__sidebar">{ sidebar }</div>
				) : (
					<Drawer
						open={ sidebarOpen }
						onClose={ () => setSidebarOpen( false ) }
						title={ __(
							'Tour settings',
							'vistour-virtual-tour-builder'
						) }
					>
						{ sidebar }
					</Drawer>
				) }
			</div>

			{ null !== editingHotspot && scene?.hotspots[ editingHotspot ] && (
				<HotspotModal
					hotspot={ scene.hotspots[ editingHotspot ] }
					scenes={ tour.scenes }
					sceneIndex={ sceneIndex }
					onChange={ ( hotspot ) =>
						updateHotspot( editingHotspot, hotspot )
					}
					onRemove={ () => {
						updateScene( sceneIndex, {
							hotspots: scene.hotspots.filter(
								( _, i ) => i !== editingHotspot
							),
						} );
						setEditingHotspot( null );
					} }
					onClose={ () => setEditingHotspot( null ) }
				/>
			) }
		</div>
	);
};

/**
 * Resolve attachment URLs for scenes loaded from REST meta (IDs only).
 *
 * @param {Array}    scenes  Scenes with image_id.
 * @param {Function} setTour State setter.
 */
const resolveSceneImages = ( scenes, setTour ) => {
	scenes.forEach( ( scene, index ) => {
		if ( ! scene.image_id || scene.image_url ) {
			return;
		}
		window.wp?.media
			?.attachment( scene.image_id )
			.fetch()
			.then( ( data ) => {
				setTour( ( current ) => {
					if ( ! current ) {
						return current;
					}
					const next = current.scenes.map( ( item, i ) =>
						i === index
							? {
									...item,
									image_url:
										data.sizes?.large?.url || data.url,
							  }
							: item
					);
					return { ...current, scenes: next };
				} );
			} );
	} );
};

export default TourEditor;
