/**
 * External dependencies.
 */
import { useEffect, useState } from '@wordpress/element';
import { ChevronDown } from 'lucide-react';
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import {
	Button,
	Container,
	DropdownMenu,
	Input,
	Label,
	Loader,
	Toaster,
	Tabs,
	toast,
} from '@bsf/force-ui';

/**
 * Internal dependencies.
 */
import SettingsHeader from '@spectra-pro-admin/global-styles/components/SettingsHeader';

// Constant with all the required font-size translatable strings.
const fontSizeDetailLabels = {
	h1: __( 'Add a numeric value and select the unit for the Heading 1 font size.', 'spectra-pro' ),
	h2: __( 'Add a numeric value and select the unit for the Heading 2 font size.', 'spectra-pro' ),
	h3: __( 'Add a numeric value and select the unit for the Heading 3 font size.', 'spectra-pro' ),
	h4: __( 'Add a numeric value and select the unit for the Heading 4 font size.', 'spectra-pro' ),
	h5: __( 'Add a numeric value and select the unit for the Heading 5 font size.', 'spectra-pro' ),
	h6: __( 'Add a numeric value and select the unit for the Heading 6 font size.', 'spectra-pro' ),
	xs: __( 'Add a numeric value and select the unit for the Extra Small font size.', 'spectra-pro' ),
	sm: __( 'Add a numeric value and select the unit for the Small font size.', 'spectra-pro' ),
	md: __( 'Add a numeric value and select the unit for the Medium font size.', 'spectra-pro' ),
	lg: __( 'Add a numeric value and select the unit for the Large font size.', 'spectra-pro' ),
	xl: __( 'Add a numeric value and select the unit for the Extra Large font size.', 'spectra-pro' ),
	xxl: __( 'Add a numeric value and select the unit for the Extra Extra Large font size.', 'spectra-pro' ),
};

/**
 * Renders the font-size settings section.
 *
 * @since x.x.x
 *
 * @param {Object} props - The element props. 
 * @return {Element}     - The rendered font-size settings section.
 */
const RenderFontSizeSection = ( props ) => {
	// Destructure the required props.
	const {
		finalizedSystemFontSize,
		setFinalizedSystemFontSize,
		currentTab,
		fontSizeValue,
		setFontSizeValue,
		fontSizeUnit,
		setFontSizeUnit,
		defaultValues,
	} = props;

	// Create states to update the font size and for the timer debounce.
	const [ updateFontSize, setUpdateFontSize ] = useState( false );
	const [ timerRef, setTimerRef ] = useState( null );

	// Array of support font-size units.
	const unitOptions = [ 'rem', 'em', 'px' ];

	// An Effect to ensure that we only update the color in AJAX when the user has stopped changing colors for a while.
	useEffect( () => {
		// If the updateFontSize is not true, abandon ship.
		if ( ! updateFontSize ) {
			return;
		}
		// Clear any existing timeout.
		if ( timerRef ) {
			clearTimeout( timerRef );
		}
	
		// Set new timeout for stability check.
		const newTimer = setTimeout( () => {
			const isDestructive = 'destructive' === updateFontSize;
			updateSystemFontSize( isDestructive );
			setUpdateFontSize( false );
		}, 1000 );
	
		// Store new timer reference.
		setTimerRef( newTimer );
	
		// Cleanup function.
		return () => {
			if ( newTimer ) {
				clearTimeout( newTimer );
			}
		};
	}, [ fontSizeValue, fontSizeUnit ] );

	// An AJAX event to update the Global Styles system font size variables.
	const updateSystemFontSize = ( isDestructive ) => {
		// Create the form data, and append the required data.
        const formData = new window.FormData();
        formData.append( 'action', 'spectra_pro_gs_system_fontsize_variables' );
        formData.append( 'security', window.spectra_admin_gs.ajax_nonce );
        formData.append( 'type', currentTab );
        formData.append( 'value', fontSizeValue );
        formData.append( 'unit', fontSizeUnit );
		formData.append( 'is_destructive', isDestructive ? 'yes' : 'no' );

		// Fetch the Ajax response.
        apiFetch( {
            url: window.spectra_admin_gs.ajax_url,
            method: 'POST',
            body: formData,
        } ).then( ( fetched ) => {
			if ( fetched?.success && fetched?.data?.saved_type ) {
				const data = fetched.data;

				// If it was successful, get the fetched font size details.
				const fetchedFontSize = {
					value: data?.saved_variable?.value,
					unit: data?.saved_variable?.unit,
					destroyed: data?.destroyed,
				}

				// If all 3 properties are not available, throw an error.
				if ( ! fetchedFontSize.destroyed && ! fetchedFontSize.unit && ! fetchedFontSize.value ) {
					throw new Error( __( 'Save/Reset was not possible.', 'spectra-pro' ) );
				}

				// Update the font size, either with the new detauls, or the detaults if it was destroyed.
				const updatedSystemFontSizes = {
					...finalizedSystemFontSize,
					[ data.saved_type ]: {
						value: fetchedFontSize.destroyed ? defaultValues[ data.saved_type ].value : fetchedFontSize.value,
						unit: fetchedFontSize.destroyed ? defaultValues[ data.saved_type ].unit : fetchedFontSize.unit,
					},
				};

				// Set the finalized font size, and update the localized font sizes.
				setFinalizedSystemFontSize( { ...updatedSystemFontSizes } );
				window.spectra_admin_gs.system_variables.fontsize = { ...updatedSystemFontSizes };
				toast.success( data.title, { description: data.description } );
			} else {
				toast.error( fetched?.data?.title || __( 'Font Size Not Saved.', 'spectra-pro' ), { description: fetched?.data?.description  } );
			}
        } ).catch( e => {
			// If the error thrown is not formatted as an error, make it one.
			if ( ! ( e instanceof Error ) ) {
				e = new Error( e );
			}
			console.error( e ); //eslint-disable-line no-console
			toast.error( __( 'Something went wrong.', 'spectra-pro' ), { description: e.message } );
		} );
	};

	// Function to update the font-size valye when the value changes.
	const handlefontSizeValueChange = ( value ) => {
		setFontSizeValue( value );
		setUpdateFontSize( true );
	};

	// Function to update the font-size unit when the unit changes.
	const handlefontSizeUnitChange = ( unit ) => {
		setFontSizeUnit( unit );
		setUpdateFontSize( true );
	};

	// Function to reset the font size.
	const resetFontSize = ( unit, value ) => {
		setFontSizeValue( value );
		setFontSizeUnit( unit );
		setUpdateFontSize( 'destructive' );
	};

	// Render the font size settings.
	return (
		<Container align="center" className="mb-0.5 w-full flex justify-between">
			<Container.Item className="w-full items-center flex flex-col gap-4">
				<Label className="w-full m-0 font-normal" size="sm" tag="p" variant="help">
					{ fontSizeDetailLabels[ currentTab ] }
				</Label>
				<div className="w-full flex items-center gap-4">
					<Input size="sm" type="text" value={ fontSizeValue } onChange={ handlefontSizeValueChange } />
					<DropdownMenu>
						<DropdownMenu.Trigger>
							<Button size="sm" variant="outline" icon={ <ChevronDown size={ 16 }/> } iconPosition="right">
								<span className='uppercase'>{ fontSizeUnit }</span>
							</Button>
						</DropdownMenu.Trigger>
						<DropdownMenu.ContentWrapper>
							<DropdownMenu.Content>
								<DropdownMenu.List heading={ __( 'Units', 'spectra-pro' ) }>
									{ unitOptions.map( ( unitOption, index ) => (
										<DropdownMenu.Item
											key={ index }
											className='uppercase text-sm'
											onClick={ () => unitOption === fontSizeUnit ? null : handlefontSizeUnitChange( unitOption ) }
											disabled={ unitOption === fontSizeUnit }
										>
											{ unitOption }
										</DropdownMenu.Item>
									) ) }
								</DropdownMenu.List>
							</DropdownMenu.Content>
						</DropdownMenu.ContentWrapper>
					</DropdownMenu>
					{ ( fontSizeUnit !== defaultValues[ currentTab ].unit || fontSizeValue !== defaultValues[ currentTab ].value ) && (
						<Button
							size="sm"
							onClick={ () => { resetFontSize( defaultValues[ currentTab ].unit, defaultValues[ currentTab ].value ) } }
						>
							<span>{ __( 'Reset', 'spectra-pro' ) }</span>
						</Button>
					) }
					{ updateFontSize && <Loader size="sm" /> }
				</div>
			</Container.Item>
		</Container>
	);
};

/**
 * Renders the font size preview.
 *
 * @since x.x.x
 *
 * @param {Object} props                         - The element props. 
 * @param {Object} props.finalizedSystemFontSize - The finalized system font sizes.
 * @param {string} props.currentTab              - The current font settings tab.
 * @return {Element}                             - The rendered custom variables settings page.
 */
const RenderAllFontSizePreview = ( { finalizedSystemFontSize, currentTab } ) => (
	<div className="flex flex-col items-start gap-4">
		<Label
			tag="h1"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.h1.value + finalizedSystemFontSize.h1.unit,
				color: 'h1' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Heading 1', 'spectra-pro' ) }
		</Label>
		<Label
			tag="h2"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.h2.value + finalizedSystemFontSize.h2.unit,
				color: 'h2' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Heading 2', 'spectra-pro' ) }
		</Label>
		<Label
			tag="h3"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.h3.value + finalizedSystemFontSize.h3.unit,
				color: 'h3' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Heading 3', 'spectra-pro' ) }
		</Label>
		<Label
			tag="h4"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.h4.value + finalizedSystemFontSize.h4.unit,
				color: 'h4' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Heading 4', 'spectra-pro' ) }
		</Label>
		<Label
			tag="h5"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.h5.value + finalizedSystemFontSize.h5.unit,
				color: 'h5' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Heading 5', 'spectra-pro' ) }
		</Label>
		<Label
			tag="h6"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.h6.value + finalizedSystemFontSize.h6.unit,
				color: 'h6' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Heading 6', 'spectra-pro' ) }
		</Label>
		<Label
			tag="p"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.xs.value + finalizedSystemFontSize.xs.unit,
				color: 'xs' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of an Extra Small paragraph.', 'spectra-pro' ) }
		</Label>
		<Label
			tag="p"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.sm.value + finalizedSystemFontSize.sm.unit,
				color: 'sm' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Small paragraph.', 'spectra-pro' ) }
		</Label>
		<Label
			tag="p"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.md.value + finalizedSystemFontSize.md.unit,
				color: 'md' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Medium paragraph. (This should be your default)', 'spectra-pro' ) }
		</Label>
		<Label
			tag="p"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.lg.value + finalizedSystemFontSize.lg.unit,
				color: 'lg' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of a Large paragraph.', 'spectra-pro' ) }
		</Label>
		<Label
			tag="p"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.xl.value + finalizedSystemFontSize.xl.unit,
				color: 'xl' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of an Extra Large paragraph.', 'spectra-pro' ) }
		</Label>
		<Label
			tag="p"
			style={ {
				margin: 0,
				padding: 0,
				lineHeight: 1.25,
				fontSize: finalizedSystemFontSize.xxl.value + finalizedSystemFontSize.xxl.unit,
				color: 'xxl' === currentTab && '#6005ff',
			} }
		>
			{ __( 'Preview of an Extra Extra Large paragraph.', 'spectra-pro' ) }
		</Label>
	</div>
); 

/**
 * Renders the font-size settings page.
 *
 * @since x.x.x
 *
 * @return {Element}     - The rendered font-size settings page.
 */
const SystemFontSizeVars = () => {

	// Get the saved system fontsizes and the defaults from localization.
	const savedFontSizes = window.spectra_admin_gs?.system_variables?.fontsize;
	const defaultValues = window.spectra_admin_gs.system_defaults.fontsize;

	// Create the finalized fontsizes.
	const [ finalizedSystemFontSize, setFinalizedSystemFontSize ] = useState( {
		h1: {
			value: savedFontSizes?.h1?.value || defaultValues.h1.value,
			unit: savedFontSizes?.h1?.unit || defaultValues.h1.unit,
		},
		h2: {
			value: savedFontSizes?.h2?.value || defaultValues.h2.value,
			unit: savedFontSizes?.h2?.unit || defaultValues.h2.unit,
		},
		h3: {
			value: savedFontSizes?.h3?.value || defaultValues.h3.value,
			unit: savedFontSizes?.h3?.unit || defaultValues.h3.unit,
		},
		h4: {
			value: savedFontSizes?.h4?.value || defaultValues.h4.value,
			unit: savedFontSizes?.h4?.unit || defaultValues.h4.unit,
		},
		h5: {
			value: savedFontSizes?.h5?.value || defaultValues.h5.value,
			unit: savedFontSizes?.h5?.unit || defaultValues.h5.unit,
		},
		h6: {
			value: savedFontSizes?.h6?.value || defaultValues.h6.value,
			unit: savedFontSizes?.h6?.unit || defaultValues.h6.unit,
		},
		xs: {
			value: savedFontSizes?.xs?.value || defaultValues.xs.value,
			unit: savedFontSizes?.xs?.unit || defaultValues.xs.unit,
		},
		sm: {
			value: savedFontSizes?.sm?.value || defaultValues.sm.value,
			unit: savedFontSizes?.sm?.unit || defaultValues.sm.unit,
		},
		md: {
			value: savedFontSizes?.md?.value || defaultValues.md.value,
			unit: savedFontSizes?.md?.unit || defaultValues.md.unit,
		},
		lg: {
			value: savedFontSizes?.lg?.value || defaultValues.lg.value,
			unit: savedFontSizes?.lg?.unit || defaultValues.lg.unit,
		},
		xl: {
			value: savedFontSizes?.xl?.value || defaultValues.xl.value,
			unit: savedFontSizes?.xl?.unit || defaultValues.xl.unit,
		},
		xxl: {
			value: savedFontSizes?.xxl?.value || defaultValues.xxl.value,
			unit: savedFontSizes?.xxl?.unit || defaultValues.xxl.unit,
		},
	} );

	// Create the required states.
	const [ fontSizeType, setFontSizeType ] = useState( 'text' );
	const [ currentTab, setCurrentTab ] = useState( 'md' );
	const [ fontSizeValue, setFontSizeValue ] = useState( finalizedSystemFontSize[ currentTab ]?.value );
	const [ fontSizeUnit, setFontSizeUnit ] = useState( finalizedSystemFontSize[ currentTab ]?.unit );

	// The possible sizing tabs to use, based on the type tabs.
	const useTabs = {
		text: [ 'xs', 'sm', 'md', 'lg', 'xl', 'xxl' ],
		heading: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ],
	};

	// Render the system font size variable settings page.
	return (
		<>
			<Toaster position="top-right" design="stack" theme="dark" autoDismiss={ true } dismissAfter={ 2000 } />
			<SettingsHeader
				heading={ __( 'Font Size', 'spectra-pro' ) }
				description={ __( 'Configure the system defaults for font sizes.', 'spectra-pro' ) }
			>
				<div className="flex flex-col items-end gap-2">
					<div style={ { width: 'fit-content' } }>
						<Tabs.Group
							activeItem={ fontSizeType }
							iconPosition="left"
							onChange={ ( { value } ) => {
								const firstTabToShow = 'text' ===  value.slug ? useTabs[ value.slug ][ 2 ] : useTabs[ value.slug ][ 0 ];
								setFontSizeType( value.slug );
								setCurrentTab( firstTabToShow );
								setFontSizeValue( finalizedSystemFontSize[ firstTabToShow ]?.value );
								setFontSizeUnit( finalizedSystemFontSize[ firstTabToShow ]?.unit );
							} }
							orientation="horizontal"
							size="sm"
							variant="pill"
							width="auto"
						>
							<Tabs.Tab slug='text' text={ __( 'Text', 'spectra-pro' ) } />
							<Tabs.Tab slug='heading' text={ __( 'Headings', 'spectra-pro' ) } />
						</Tabs.Group>
					</div>
					<Tabs.Group
						activeItem={ currentTab }
						iconPosition="left"
						onChange={ ( { value } ) => {
							setCurrentTab( value.slug );
							setFontSizeValue( finalizedSystemFontSize[ value.slug ]?.value );
							setFontSizeUnit( finalizedSystemFontSize[ value.slug ]?.unit );
						} }
						orientation="horizontal"
						size="xs"
						variant="pill"
						width="auto"
					>
						{ useTabs[ fontSizeType ].map( ( fontSize, index ) => (
							<Tabs.Tab key={ index } slug={ fontSize } text={ fontSize.toUpperCase() } />
						) ) }
					</Tabs.Group>
				</div>
			</SettingsHeader>
			<RenderFontSizeSection
				{ ...{
					finalizedSystemFontSize,
					setFinalizedSystemFontSize,
					currentTab,
					setCurrentTab,
					fontSizeValue,
					setFontSizeValue,
					fontSizeUnit,
					setFontSizeUnit,
					defaultValues,
				} }
			/>
			<RenderAllFontSizePreview { ...{ finalizedSystemFontSize, currentTab } }/>
		</>
	);

};

// Export the SystemFontSizeVars by default.
export default SystemFontSizeVars;
