/**
 * 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 spacingDetailLabels = {
	xs: __( 'Add a numeric value and select the unit for the Extra Small spacing.', 'spectra-pro' ),
	sm: __( 'Add a numeric value and select the unit for the Small spacing.', 'spectra-pro' ),
	md: __( 'Add a numeric value and select the unit for the Medium spacing.', 'spectra-pro' ),
	lg: __( 'Add a numeric value and select the unit for the Large spacing.', 'spectra-pro' ),
	xl: __( 'Add a numeric value and select the unit for the Extra Large spacing.', 'spectra-pro' ),
	xxl: __( 'Add a numeric value and select the unit for the Extra Extra Large spacing.', 'spectra-pro' ),
};

// All the preview area colors.
const previewAreaColors = {
	padding: {
		light: '#f7fee7', // Tailwind 4.0 Lime 50.
		dark: '#bef264', // Tailwind 4.0 Lime 300.
	},
	margin: {
		light: '#fff7ed', // Tailwind 4.0 Orange 50.
		dark: '#fdba74', // Tailwind 4.0 Orange 300.
	},
	gap: {
		light: '#f5f3ff', // Tailwind 4.0 Violet 50.
		dark: '#c4b5fd', // Tailwind 4.0 Violet 300.
	},
};

/**
 * Render the striped background based.
 *
 * @since x.x.x
 *
 * @param {string} type      - The gradient type.
 * @param {Array}  endPoints - The endpoints for the four gradient positions. 
 * @return {Object}          - An object containing the background style with the required gradient.
 */
const stripedBackground = ( type, endPoints = [ '5px', '6px', '7px', '12px' ] ) => ( {
	background: `repeating-linear-gradient(45deg, ${ previewAreaColors[ type ].light }, ${ previewAreaColors[ type ].light } ${ endPoints[0] }, ${ previewAreaColors[ type ].dark } ${ endPoints[1] }, ${ previewAreaColors[ type ].light } ${ endPoints[2] }, ${ previewAreaColors[ type ].light } ${ endPoints[3] })`,
} );


/**
 * Renders the ledger item for the spacing preview.
 *
 * @since x.x.x
 *
 * @param {Object} props - The element props. 
 * @return {Element}     - The rendered color box with the label.
 */
const RenderLedgerItem = ( props ) => {
	// Destructure the required props.
	const {
		type,
		ledgerItem,
	} = props;

	// Render the box and the label.
	return (
		<div className="flex items-center justify-center gap-2">
			<div style={ {
				width: '1rem',
				height: '1rem',
				border: '1px solid #aaa',
				borderRadius: '4px',
				...stripedBackground( type, [ '2px', '3px', '4px', '6px' ] ),
			} }/>
			<Label size="sm" variant="help" tag="p">
				{ ledgerItem }
			</Label>
		</div>
	);
};

/**
 * Renders the spacing settings section.
 *
 * @since x.x.x
 *
 * @param {Object} props - The element props. 
 * @return {Element}     - The rendered spacing settings section.
 */
const RenderSpacingSection = ( props ) => {
	// Destructure the required props.
	const {
		finalizedSystemSpacing,
		setFinalizedSystemSpacing,
		currentTab,
		setCurrentTab,
		spacingValue,
		setSpacingValue,
		spacingUnit,
		setSpacingUnit,
		setBuiltSpacingUnit,
		defaultValues,
	} = props;

	// Set a state to update the spacing, and for the timer debounce.
	const [ updateSpacing, setUpdateSpacing ] = useState( false );
	const [ timerRef, setTimerRef ] = useState( null );

	// An array of the valid units for spacing.
	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 spacing was not updated, abandon ship.
		if ( ! updateSpacing ) {
			return;
		}
		// Clear any existing timeout.
		if ( timerRef ) {
			clearTimeout( timerRef );
		}

		// Set new timeout for stability check,
		const newTimer = setTimeout( () => {
			const isDestructive = 'destructive' === updateSpacing;
			updateSystemSpacing( isDestructive );
			setUpdateSpacing( false );
		}, 1000 );

		// Store new timer reference,
		setTimerRef( newTimer );

		// Cleanup function,
		return () => {
			if ( newTimer ) {
				clearTimeout( newTimer );
			}
		};
	}, [ spacingValue, spacingUnit ] );

	// An AJAX event to update the Global Styles system spacing variables.
	const updateSystemSpacing = ( isDestructive ) => {
		// Create a form and apped the required data.
        const formData = new window.FormData();
        formData.append( 'action', 'spectra_pro_gs_system_spacing_variables' );
        formData.append( 'security', window.spectra_admin_gs.ajax_nonce );
        formData.append( 'type', currentTab );
        formData.append( 'value', spacingValue );
        formData.append( 'unit', spacingUnit );
		formData.append( 'is_destructive', isDestructive ? 'yes' : 'no' );

		// Fetch the response from the Ajax request.
        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 this was successful, get the required spacing details.
				const fetchedSpacing = {
					value: data?.saved_variable?.value,
					unit: data?.saved_variable?.unit,
					destroyed: data?.destroyed,
				}

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

				// Create the built spacing vased on the fetched details, or the defaults if it was destroyed.
				const builtSpacing = {
					value: fetchedSpacing.destroyed ? defaultValues[ data.saved_type ].value : fetchedSpacing.value,
					unit: fetchedSpacing.destroyed ? defaultValues[ data.saved_type ].unit : fetchedSpacing.unit
				}

				// Update the system spacing.
				const updatedSystemSpacing = {
					...finalizedSystemSpacing,
					[ data.saved_type ]: {
						value: builtSpacing.value,
						unit: builtSpacing.unit,
					},
				};

				// Set the finalized system spacing, and update the localized spacing.
				setFinalizedSystemSpacing( { ...updatedSystemSpacing } );
				window.spectra_admin_gs.system_variables.spacing = { ...updatedSystemSpacing };
				setBuiltSpacingUnit( builtSpacing.value + builtSpacing.unit );
				toast.success( data.title, { description: data.description } );
			} else {
				toast.error( fetched?.data?.title || __( 'Spacing 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 spacing when the value changes.
	const handleSpacingValueChange = ( value ) => {
		setSpacingValue( value );
		setUpdateSpacing( true );
	};

	// Function to update the spacing when the unit changes.
	const handleSpacingUnitChange = ( unit ) => {
		setSpacingUnit( unit );
		setUpdateSpacing( true );
	};

	// Function to reset the spacing.
	const resetSpacing = ( unit, value ) => {
		setSpacingValue( value );
		setSpacingUnit( unit );
		setUpdateSpacing( 'destructive' );
	};

	// Render the spacing section.
	return (
		<>
			<SettingsHeader
				heading={ __( 'Spacing', 'spectra-pro' ) }
				description={ __( 'Configure the system defaults for spacing across all classes.', 'spectra-pro' ) }
			>
				<Tabs.Group
					activeItem={ currentTab }
					iconPosition="left"
					onChange={ ( { value } ) => {
						setCurrentTab( value.slug );
						setSpacingValue( finalizedSystemSpacing[ value.slug ]?.value );
						setSpacingUnit( finalizedSystemSpacing[ value.slug ]?.unit );
						setBuiltSpacingUnit( finalizedSystemSpacing[ value.slug ]?.value + finalizedSystemSpacing[ value.slug ]?.unit );
					} }
					orientation="horizontal"
					size="xs"
					variant="pill"
					width="auto"
				>
					<Tabs.Tab slug="xs" text="XS" />
					<Tabs.Tab slug="sm" text="SM" />
					<Tabs.Tab slug="md" text="MD" />
					<Tabs.Tab slug="lg" text="LG" />
					<Tabs.Tab slug="xl" text="XL" />
					<Tabs.Tab slug="xxl" text="XXL" />
				</Tabs.Group>
			</SettingsHeader>
			<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">
						{ spacingDetailLabels[ currentTab ] }
					</Label>
					<div className='w-full flex justify-between'>
						<div className="flex items-center gap-4">
							<Input size="sm" type="text" value={ spacingValue } onChange={ handleSpacingValueChange } />
							<DropdownMenu>
								<DropdownMenu.Trigger>
									<Button size="sm" variant="outline" icon={ <ChevronDown size={ 16 }/> } iconPosition="right">
										<span className='uppercase'>{ spacingUnit }</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 === spacingUnit ? null : handleSpacingUnitChange( unitOption ) }
													disabled={ unitOption === spacingUnit }
												>
													{ unitOption }
												</DropdownMenu.Item>
											) ) }
										</DropdownMenu.List>
									</DropdownMenu.Content>
								</DropdownMenu.ContentWrapper>
							</DropdownMenu>
							{ ( spacingUnit !== defaultValues[ currentTab ].unit || spacingValue !== defaultValues[ currentTab ].value ) && (
								<Button
									size="sm"
									onClick={ () => { resetSpacing( defaultValues[ currentTab ].unit, defaultValues[ currentTab ].value ) } }
								>
									<span>{ __( 'Reset', 'spectra-pro' ) }</span>
								</Button>
							) }
							{ updateSpacing && <Loader size="sm" /> }
						</div>
						<div className="flex items-center justify-end gap-4">
							<RenderLedgerItem type="margin" ledgerItem={ __( 'Margin', 'spectra-pro' ) } />
							<RenderLedgerItem type="padding" ledgerItem={ __( 'Padding', 'spectra-pro' ) } />
							<RenderLedgerItem type="gap" ledgerItem={ __( 'Gap', 'spectra-pro' ) } />
						</div>
					</div>
				</Container.Item>
			</Container>
		</>
	);
};

/**
 * Renders the spacing preview
 *
 * @since x.x.x
 *
 * @param {Object} props - The element props. 
 * @return {Element}     - The rendered spacing preview.
 */
const RenderSpacingPreview = ( props ) => {
	// Destructure the required props.
	const {
		builtSpacingValue,
	} = props;

	// REnder the spacing section.
	return (
		<div className="flex flex-wrap items-center justify-center gap-4">
			{/* The element that simulates the margin preview. */}
			<div
				className="flex items-center justify-center transition-all"
				style={ {
					...stripedBackground( 'margin' ),
					padding: builtSpacingValue,
				} }
			>
				{/* The preview element, with the padding preview. */}
				<div
					className="flex items-center justify-center transition-all overflow-hidden"
					style={ {
						border: '1px solid #aaa',
						borderRadius: '4px',
						...stripedBackground( 'padding' ),
						padding: builtSpacingValue,
					} }
				>
					{/* The wrapper inside the element preview, with the gap preview. */}
					<div
						className="bg-white flex flex-col items-center"
						style={ {
							...stripedBackground( 'gap' ),
							gap: builtSpacingValue,
						} }
					>
						{ window.spectra_admin_gs?.pro_plugin_url && (
							<img
								src={ `${ window.spectra_admin_gs?.pro_plugin_url }assets/images/placeholder.png` }
								style={ { width: '10rem' } }
								alt={ __( 'Placeholder image', 'spectra-pro' ) }
							/>
						) }
						<Label className="bg-white w-full flex justify-center m-0 p-2 font-normal" size="sm" tag="p" variant="help">
							{ __( 'Spacing Preview', 'spectra-pro' ) }
						</Label>
					</div>
				</div>
			</div>
		</div>
	);
};


/**
 * Renders the system Spacing settings.
 *
 * @since x.x.x
 *
 * @return {Element} - The rendered system settings page.
 */
const SystemSpacingVars = () => {

	// Get the saved system spacing and the defaults from localization.
	const savedSpacing = window.spectra_admin_gs?.system_variables?.spacing;
	const defaultValues = window.spectra_admin_gs.system_defaults.spacing;

	// Create the finalized spacing.
	const [ finalizedSystemSpacing, setFinalizedSystemSpacing ] = useState( {
		xs: {
			value: savedSpacing?.xs?.value || defaultValues.xs.value,
			unit: savedSpacing?.xs?.unit || defaultValues.xs.unit,
		},
		sm: {
			value: savedSpacing?.sm?.value || defaultValues.sm.value,
			unit: savedSpacing?.sm?.unit || defaultValues.sm.unit,
		},
		md: {
			value: savedSpacing?.md?.value || defaultValues.md.value,
			unit: savedSpacing?.md?.unit || defaultValues.md.unit,
		},
		lg: {
			value: savedSpacing?.lg?.value || defaultValues.lg.value,
			unit: savedSpacing?.lg?.unit || defaultValues.lg.unit,
		},
		xl: {
			value: savedSpacing?.xl?.value || defaultValues.xl.value,
			unit: savedSpacing?.xl?.unit || defaultValues.xl.unit,
		},
		xxl: {
			value: savedSpacing?.xxl?.value || defaultValues.xxl.value,
			unit: savedSpacing?.xxl?.unit || defaultValues.xxl.unit,
		},
	} );

	// Create the required states.
	const [ currentTab, setCurrentTab ] = useState( 'xs' );
	const [ spacingValue, setSpacingValue ] = useState( finalizedSystemSpacing[ currentTab ]?.value );
	const [ spacingUnit, setSpacingUnit ] = useState( finalizedSystemSpacing[ currentTab ]?.unit );
	const [ builtSpacingValue, setBuiltSpacingUnit ] = useState( spacingValue + spacingUnit );

	// Render the system spacing page.
	return (
		<>
			<Toaster position="top-right" design="stack" theme="dark" autoDismiss={ true } dismissAfter={ 2000 } />
			<RenderSpacingSection
				{ ...{
					finalizedSystemSpacing,
					setFinalizedSystemSpacing,
					currentTab,
					setCurrentTab,
					spacingValue,
					setSpacingValue,
					spacingUnit,
					setSpacingUnit,
					builtSpacingValue,
					setBuiltSpacingUnit,
					defaultValues,
				} }
			/>
			<RenderSpacingPreview { ...{ builtSpacingValue } }/>
		</>
	);

};

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