/**
 * Media library / WordPress oEmbed field (image, video, or embed).
 */
import { Button, Spinner, TextControl } from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';
import { useEffect, useRef, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { normalizeEmbedUrl, normalizeMedia, prepareEmbedWrapperInner, isDirectVideoEmbedProvider } from '../../interpolate';
import SearchableSelect from '../SearchableSelect';
import BmMediaUpload from './BmMediaUpload';
const TYPE_OPTIONS = [
	{ label: __( 'Image', 'joc-blockora-morph' ), value: 'image' },
	{ label: __( 'Video', 'joc-blockora-morph' ), value: 'video' },
	{ label: __( 'Embed', 'joc-blockora-morph' ), value: 'iframe' },
];

/**
 * @param {object} field
 * @return {string[]}
 */
function allowedTypes( field = {} ) {
	if ( Array.isArray( field.mediaTypes ) && field.mediaTypes.length ) {
		return field.mediaTypes.filter( ( t ) =>
			[ 'image', 'video', 'iframe' ].includes( t )
		);
	}
	if ( field.mediaType === 'video' ) {
		return [ 'video' ];
	}
	if ( field.mediaType === 'iframe' ) {
		return [ 'iframe' ];
	}
	if ( field.mediaType === 'image' ) {
		return [ 'image' ];
	}
	return [ 'image', 'video', 'iframe' ];
}

/**
 * Fetch WordPress oEmbed preview for a URL.
 *
 * @param {string} url
 * @return {Promise<{ html: string, title: string, providerName: string }>}
 */
async function fetchOEmbed( url ) {
	const raw = String( url || '' ).trim();
	if ( ! raw ) {
		throw new Error( 'empty' );
	}
	const data = await apiFetch( {
		path: `/oembed/1.0/proxy?url=${ encodeURIComponent( raw ) }`,
	} );
	return {
		html: data?.html ? String( data.html ) : '',
		title: data?.title ? String( data.title ) : '',
		providerName: data?.provider_name
			? String( data.provider_name )
			: '',
	};
}

/**
 * WordPress-style embed URL field with oEmbed preview.
 *
 * @param {object}   props
 * @param {object}   props.media
 * @param {Function} props.onChange
 * @param {object}   props.field
 * @param {Function} props.clearMedia
 */
function EmbedMediaControls( { media, onChange, field, clearMedia } ) {
	const [ draftUrl, setDraftUrl ] = useState( media.url || '' );
	const [ status, setStatus ] = useState( 'idle' ); // idle|loading|error
	const [ errorMsg, setErrorMsg ] = useState( '' );
	const reqId = useRef( 0 );

	useEffect( () => {
		setDraftUrl( media.url || '' );
	}, [ media.url ] );

	const commitEmbed = async ( nextUrl ) => {
		const url = String( nextUrl || '' ).trim();
		if ( ! url ) {
			onChange(
				normalizeMedia(
					{
						id: 0,
						url: '',
						alt: '',
						mime: '',
						type: 'iframe',
						embedHtml: '',
						providerName: '',
					},
					field
				)
			);
			setStatus( 'idle' );
			setErrorMsg( '' );
			return;
		}

		const id = ++reqId.current;
		setStatus( 'loading' );
		setErrorMsg( '' );
		try {
			const preview = await fetchOEmbed( url );
			if ( id !== reqId.current ) {
				return;
			}
			if ( ! preview.html ) {
				throw new Error( 'no-html' );
			}
			onChange(
				normalizeMedia(
					{
						id: 0,
						url,
						alt: media.alt || preview.title || '',
						mime: '',
						type: 'iframe',
						embedHtml: preview.html,
						providerName: preview.providerName,
					},
					field
				)
			);
			setStatus( 'idle' );
		} catch ( err ) {
			if ( id !== reqId.current ) {
				return;
			}
			// Keep URL; fall back to URL-rewrite iframe on render.
			onChange(
				normalizeMedia(
					{
						id: 0,
						url,
						alt: media.alt || '',
						mime: '',
						type: 'iframe',
						embedHtml: '',
						providerName: '',
					},
					field
				)
			);
			setStatus( 'error' );
			setErrorMsg(
				__(
					'Sorry, this URL could not be embedded via WordPress oEmbed. It may still render as a direct iframe for YouTube/Vimeo.',
					'joc-blockora-morph'
				)
			);
		}
	};

	const hasPreview = Boolean( media.embedHtml || media.url );

	return (
		<>
			<div className="bm-media-field__embed-form">
				<TextControl
					label={ __( 'Embed URL', 'joc-blockora-morph' ) }
					help={ __(
						'Paste a link to YouTube, Vimeo, Twitter, or another oEmbed provider — same as the WordPress Embed block.',
						'joc-blockora-morph'
					) }
					value={ draftUrl }
					onChange={ ( next ) => {
						setDraftUrl( next );
						if ( status === 'error' ) {
							setStatus( 'idle' );
							setErrorMsg( '' );
						}
					} }
					onKeyDown={ ( event ) => {
						if ( event.key === 'Enter' ) {
							event.preventDefault();
							commitEmbed( draftUrl );
						}
					} }
					type="url"
					placeholder="https://"
					__nextHasNoMarginBottom
					__next40pxDefaultSize
				/>
				<div className="bm-media-field__embed-actions">
					<Button
						variant="primary"
						disabled={ status === 'loading' || ! draftUrl.trim() }
						onClick={ () => commitEmbed( draftUrl ) }
					>
						{ status === 'loading'
							? __( 'Embedding…', 'joc-blockora-morph' )
							: __( 'Embed', 'joc-blockora-morph' ) }
					</Button>
					{ hasPreview ? (
						<Button
							variant="tertiary"
							isDestructive
							disabled={ status === 'loading' }
							onClick={ () => {
								setDraftUrl( '' );
								clearMedia();
								setStatus( 'idle' );
								setErrorMsg( '' );
							} }
						>
							{ __( 'Remove', 'joc-blockora-morph' ) }
						</Button>
					) : null }
				</div>
			</div>

			{ status === 'loading' ? (
				<div className="bm-media-field__embed-status">
					<Spinner />
					<span>{ __( 'Fetching embed preview…', 'joc-blockora-morph' ) }</span>
				</div>
			) : null }

			{ status === 'error' && errorMsg ? (
				<p className="bm-media-field__embed-error" role="alert">
					{ errorMsg }
				</p>
			) : null }

			{ hasPreview && status !== 'loading' ? (
				<div className="bm-media-field__picker">
					<div
						className={
							'bm-media-field__thumb bm-media-field__thumb--iframe' +
							( media.embedHtml
								? ' bm-media-field__thumb--oembed'
								: '' )
						}
						aria-hidden="true"
					>
						{ media.embedHtml && ! isDirectVideoEmbedProvider( media.url ) ? (
							<div
								className="bm-media-field__oembed"
								dangerouslySetInnerHTML={ {
									__html: prepareEmbedWrapperInner( media.embedHtml ),
								} }
							/>
						) : (
							<iframe
								src={ normalizeEmbedUrl( media.url ) }
								title={
									media.alt ||
									__( 'Embed preview', 'joc-blockora-morph' )
								}
								loading="lazy"
								referrerPolicy="strict-origin-when-cross-origin"
								allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
								allowFullScreen
								tabIndex={ -1 }
							/>
						) }
					</div>
					<div className="bm-media-field__side">
						<span className="bm-media-field__side-label">
							{ media.providerName ||
								__( 'Embed', 'joc-blockora-morph' ) }
						</span>
						{ media.url ? (
							<span className="bm-media-field__embed-url" title={ media.url }>
								{ media.url }
							</span>
						) : null }
					</div>
				</div>
			) : null }

			<TextControl
				label={ __( 'Title (accessibility)', 'joc-blockora-morph' ) }
				value={ media.alt }
				onChange={ ( next ) =>
					onChange(
						normalizeMedia(
							{ ...media, type: 'iframe', alt: next },
							field
						)
					)
				}
				__nextHasNoMarginBottom
				__next40pxDefaultSize
			/>
		</>
	);
}

/**
 * @param {object}   props
 * @param {string}   props.label
 * @param {object}   props.value
 * @param {Function} props.onChange
 * @param {object}   [props.field]
 */
export default function MediaField( { label, value, onChange, field = {} } ) {
	const types = allowedTypes( field );
	const media = normalizeMedia( value || {}, field );
	const kind = types.includes( media.type ) ? media.type : types[ 0 ] || 'image';
	const canSwitch = types.length > 1;
	const libraryTypes =
		kind === 'video' ? [ 'video' ] : kind === 'image' ? [ 'image' ] : [];

	const setType = ( nextType ) => {
		const next = nextType || types[ 0 ] || 'image';
		onChange(
			normalizeMedia(
				{
					id: 0,
					url: '',
					alt: '',
					mime: '',
					type: next,
					embedHtml: '',
					providerName: '',
				},
				field
			)
		);
	};

	const applyFromAttachment = ( attachment ) => {
		if ( ! attachment ) {
			return;
		}
		onChange(
			normalizeMedia(
				{
					id: attachment.id,
					url: attachment.url,
					alt: attachment.alt || attachment.title || '',
					mime: attachment.mime || attachment.mime_type || '',
					type: kind,
					embedHtml: '',
					providerName: '',
				},
				field
			)
		);
	};

	const clearMedia = () =>
		onChange(
			normalizeMedia(
				{
					type: kind,
					embedHtml: '',
					providerName: '',
				},
				field
			)
		);

	const typeOptions = TYPE_OPTIONS.filter( ( o ) => types.includes( o.value ) );
	const kindLabel =
		kind === 'video'
			? __( 'Video', 'joc-blockora-morph' )
			: kind === 'iframe'
			? __( 'Embed', 'joc-blockora-morph' )
			: __( 'Image', 'joc-blockora-morph' );

	return (
		<div className="bm-field bm-media-field">
			{ label ? <span className="bm-field-label">{ label }</span> : null }

			{ canSwitch ? (
				<SearchableSelect
					label={ __( 'Media type', 'joc-blockora-morph' ) }
					className="bm-media-field__type"
					value={ kind }
					options={ typeOptions }
					onChange={ setType }
					searchableThreshold={ 1 }
				/>
			) : null }

			{ kind === 'iframe' ? (
				<EmbedMediaControls
					media={ media }
					onChange={ onChange }
					field={ field }
					clearMedia={ clearMedia }
				/>
			) : (
				<BmMediaUpload
					onSelect={ applyFromAttachment }
					allowedTypes={ libraryTypes }
					value={ media.id || undefined }
					title={
						kind === 'video'
							? __( 'Select video', 'joc-blockora-morph' )
							: __( 'Select image', 'joc-blockora-morph' )
					}
					render={ ( { open } ) =>
						media.url ? (
							<div className="bm-media-field__picker">
								<button
									type="button"
									className="bm-media-field__thumb"
									onClick={ open }
									aria-label={
										kind === 'video'
											? __(
													'Replace video',
													'joc-blockora-morph'
											  )
											: __(
													'Replace image',
													'joc-blockora-morph'
											  )
									}
								>
									{ kind === 'video' ? (
										<video
											src={ media.url }
											muted
											playsInline
										/>
									) : (
										<img
											src={ media.url }
											alt={ media.alt || '' }
										/>
									) }
								</button>
								<div className="bm-media-field__side">
									<span className="bm-media-field__side-label">
										{ kindLabel }
									</span>
									<div className="bm-media-field__actions">
										<Button
											variant="secondary"
											onClick={ open }
										>
											{ __( 'Replace', 'joc-blockora-morph' ) }
										</Button>
										<Button
											variant="tertiary"
											isDestructive
											onClick={ clearMedia }
										>
											{ __( 'Remove', 'joc-blockora-morph' ) }
										</Button>
									</div>
								</div>
							</div>
						) : (
							<div className="bm-media-field__empty">
								<Button variant="secondary" onClick={ open }>
									{ kind === 'video'
										? __( 'Select video', 'joc-blockora-morph' )
										: __( 'Select image', 'joc-blockora-morph' ) }
								</Button>
							</div>
						)
					}
				/>
			) }

			{ kind === 'image' && media.url ? (
				<TextControl
					label={ __( 'Alt text', 'joc-blockora-morph' ) }
					className="bm-media-field__alt"
					value={ media.alt }
					onChange={ ( next ) =>
						onChange(
							normalizeMedia( { ...media, alt: next }, field )
						)
					}
					__nextHasNoMarginBottom
					__next40pxDefaultSize
				/>
			) : null }
		</div>
	);
}
