/**
 * External dependencies.
 */
import { useEffect, useState, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import {
	Toaster,
	toast,
	Button,
} from '@bsf/force-ui';
import {
	Save,
} from 'lucide-react';

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

/**
 * Converts a stylesheet string to an object.
 *
 * @since x.x.x
 *
 * @param {string} cssVarSheet - A string containing the entire stylesheet content. 
 * @return {Object}            - The style object with the CSS variables as keys, and their CSS values as values.
 */
const convertToCssVarObject = ( cssVarSheet ) => {

	// Split into lines and filter empty ones and lines containing { or }.
	const lines = cssVarSheet.split( '\n' ).filter( line => {
		const trimmedLine = line.trim();
		return trimmedLine && !trimmedLine.includes( '{' ) && !trimmedLine.includes( '}' );
	} );

	// Create object to store key-value pairs.
	const cssVarObject = {};
	
	// Process each line
	lines.forEach( line => {
		const trimmedLine = line.trim();
		// Check if line starts with '--'.
		if ( trimmedLine.startsWith( '--' ) ) {
			// Extract key and value using regex.
			const match = trimmedLine.match( /^--([^:]+):(.+);/ );
			
			if ( match ) {
				// Get key and value, trimming whitespace.
				const key = match[ 1 ].trim();
				const value = match[ 2 ].trim();
				
				// Add to cssVarObject.
				cssVarObject[ key ] = value;
			}
		}
	} );

	// Return the CSS variable object.
	return cssVarObject;
};

/**
 * Convert the given stylesheet object to a string.
 *
 * @since x.x.x
 *
 * @param {Object} cssVarObject - The CSS variable object. 
 * @return {string}             - The stringified stylesheet based on the object.
 */
const convertToCssVarSheet = ( cssVarObject ) => {
    // Map over object entries and format each key-value pair.
    const lines = Object.entries( cssVarObject ).map( ( [ key, value ] ) => (
        `--${ key }: ${ value };`
	) );
    
    // Join with newlines.
    return lines.join( '\n' );
};

/**
 * Renders the custom variables settings page.
 *
 * @since x.x.x
 *
 * @return {Element} - The rendered custom variables settings page.
 */
const CustomVariables = () => {

	// Get the saved user variables from localization.
	const savedUserVariables = window.spectra_admin_gs?.user_css?.variables || {};

	// Create all the required states.
	const [ userVars, setUserVars ] = useState();
	const [ finalizedUserVars, setFinalizedUserVars ] = useState( savedUserVariables );
	const [ isSaving, setIsSaving ] = useState( false );
	const [ justSaved, setJustSaved ] = useState( false );

	// Create the required refs.
	const cssVarEditorRef = useRef( null );
	const isUserTyping = useRef( false );

	// Initialize temporary storage and load saved data
	useEffect( () => {
		if ( ! window.spectraGSTemporaryStorage ) {
			window.spectraGSTemporaryStorage = {};
		}

		// Load from temporary storage if available
		const tempVars = window.spectraGSTemporaryStorage.customVars;
		if ( tempVars ) {
			setUserVars( tempVars );
		}
	}, [] );

	// Save to temporary storage only when there are actual changes
	useEffect( () => {
		if ( ! window.spectraGSTemporaryStorage ) {
			window.spectraGSTemporaryStorage = {};
		}
		
		if ( userVars !== undefined ) {
			const currentVars = convertToCssVarSheet( finalizedUserVars );
			
			// Only update temporary storage if there are actual changes
			if ( userVars !== currentVars ) {
				window.spectraGSTemporaryStorage.customVars = userVars;
			} else {
				// Remove from temporary storage if no changes
				delete window.spectraGSTemporaryStorage.customVars;
			}
			
			// Reset justSaved state when user makes changes
			if ( justSaved ) {
				setJustSaved( false );
			}
		}
	}, [ userVars, finalizedUserVars ] );

	// An Effect to initialize the Code Mirror editor.
	useEffect( () => {
		// Get the variable editor, and the code mirror instances inside it.
		const variableEditor = document.querySelector( '#spectra-pro-gs-variable-editor' );
		const editors = variableEditor?.querySelectorAll( '.CodeMirror-wrap' );

		// If any instances exist, remove them.
		if ( editors ) {
			editors?.forEach( ( editor ) => {
				editor?.remove();
			} );
		}

		// If there's a codemirror settngs available, update the default placeholder.
		if ( wp?.codeEditor?.defaultSettings?.codemirror ) {
			wp.codeEditor.defaultSettings.codemirror.placeholder = '--color-wordpress: #007cba;\n--radius-custom: 1rem;';
		}

		// Initialize the CodeMirror editor with the required settings.
		const editor = wp?.codeEditor?.initialize( cssVarEditorRef?.current, {
			...( wp?.codeEditor?.defaultSettings?.codemirror || {} ),
		} );

		// Get hte code mirror code textarea.
		const codeMirrorEditor = document.querySelector( '#spectra-pro-gs-variable-editor .CodeMirror-code' );

		// If it exists, add the event listener to update the variables.
		if ( codeMirrorEditor ) {
			codeMirrorEditor?.addEventListener( 'keyup', function () {
				isUserTyping.current = true;
				editor?.codemirror?.save();
				const value = editor?.codemirror?.getValue();
				setUserVars( value );
				
				// Reset typing flag after a short delay
				setTimeout( () => {
					isUserTyping.current = false;
				}, 100 );
			} );
		}
	}, [ cssVarEditorRef ] );

	// Update CodeMirror editor content when userVars changes
	useEffect( () => {
		if ( userVars !== undefined && ! isUserTyping.current ) {
			const editor = document.querySelector( '#spectra-pro-gs-variable-editor .CodeMirror' );
			if ( editor && editor.CodeMirror ) {
				editor.CodeMirror.setValue( userVars );
			}
		}
	}, [ userVars ] );

	// Check if there are unsaved changes
	const hasUnsavedChanges = () => {
		const currentVars = convertToCssVarSheet( finalizedUserVars );
		return userVars !== undefined && userVars !== currentVars;
	};

	// Get button text based on current state
	const getButtonText = () => {
		if ( isSaving ) {
			return __( 'Saving…', 'spectra-pro' );
		}
		if ( justSaved ) {
			return __( 'Saved!', 'spectra-pro' );
		}
		return __( 'Save Changes', 'spectra-pro' );
	};

	// An AJAX event to update the Global Styles user variables.
	const updateUserVariables = () => {
		if ( ! hasUnsavedChanges() || isSaving ) {
			return;
		}

		setIsSaving( true );

		// Get the user variables.
		const userVariables = convertToCssVarObject( userVars );

		// Create the form data to send, and add all the required data.
        const formData = new window.FormData();
        formData.append( 'action', 'spectra_pro_gs_user_variables' );
        formData.append( 'security', window.spectra_admin_gs.ajax_nonce );
        formData.append( 'variables', JSON.stringify( userVariables ) );

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

				// If the variables were saved, update the finalized variables and the localized variables.
				setFinalizedUserVars( { ...data.saved_variables } );
				window.spectra_admin_gs.user_css.variables = { ...data.saved_variables };
				
				// Update CodeMirror with formatted content after successful save
				const formattedContent = convertToCssVarSheet( data.saved_variables );
				const editor = document.querySelector( '#spectra-pro-gs-variable-editor .CodeMirror' );
				if ( editor && editor.CodeMirror ) {
					editor.CodeMirror.setValue( formattedContent );
					setUserVars( formattedContent );
				}

				// Clear temporary storage for this component
				if ( window.spectraGSTemporaryStorage ) {
					delete window.spectraGSTemporaryStorage.customVars;
				}
				
				// Clear userVars state to prevent re-adding to temporary storage
				setUserVars( undefined );
				
				toast.success( data.title, { description: data.description } );
				setJustSaved( true );
				
				// Reset justSaved state after 2 seconds
				setTimeout( () => {
					setJustSaved( false );
				}, 1000 );
			} else {
				toast.error( fetched?.data?.title || __( 'Variables 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.message ) {
				e = new Error( e );
			}
			console.error( e ); // eslint-disable-line no-console
			toast.error( __( 'Something went wrong.', 'spectra-pro' ), { description: e.message } );
		} ).finally( () => {
			setIsSaving( false );
		} );
	};

	// Render the custom variables settings.
	return (
		<>
			<Toaster position="top-right" design="stack" theme="dark" autoDismiss={ true } dismissAfter={ 2000 } />
			<SettingsHeader
				heading={ __( 'Custom CSS Variables', 'spectra-pro' ) }
				description={ __( 'Create and configure your own CSS variables.', 'spectra-pro' ) }
				help={ __( 'All variables should start on a new line with a double hyphen.', 'spectra-pro' ) }
			/>
			<div
				id="spectra-pro-gs-variable-editor"
				style={ {
					border: '1px solid #e5e7eb',
					borderRadius: '0.375rem',
					overflow: 'hidden',
				} }
			>
				<textarea 
					ref={ cssVarEditorRef } 
				defaultValue={ 
						userVars !== undefined ? userVars : convertToCssVarSheet( finalizedUserVars )
					}
				/>
			</div>
			<div>
				<Button
					variant={ hasUnsavedChanges() ? 'primary' : 'secondary' }
					size="sm"
					icon={ <Save size={ 16 } /> }
					iconPosition="left"
					disabled={ ! hasUnsavedChanges() || isSaving }
					onClick={ updateUserVariables }
				>
					{ getButtonText() }
				</Button>
			</div>
		</>
	);

};

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