/**
 * External dependencies.
 */
import { createContext, useContext, useState, useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Context for managing dynamic content settings.
 */
const DynamicContentContext = createContext();

/**
 * Provides a context for managing dynamic content settings within the application.
 *
 * This provider maintains an internal state for various dynamic content configurations,
 * such as content type, data source, and display options. It offers functions to update
 * settings, including nested advanced settings, and reset them to their initial values.
 *
 * @since x.x.x
 *
 * @param {Object} props          - The component props.
 * @param {Node}   props.children - The child components to render within the provider.
 * @return {Element} A context provider with dynamic content settings available to consumers.
 */
export const DynamicContentProvider = ( { children } ) => {
	const initialState = {
		type: 'text', // Specifies the dynamic content type (e.g., 'text', 'image').
		source: {
			type: 'current_post', // Data source (e.g., 'current_post', 'post_type').
			field: 'post_title', // Specific field to fetch (e.g., 'post_title').
			postId: 0, // Post ID for 'post_type' source.
			postType: 'post', // Post type for 'post_type' source (e.g., 'post', 'page').
			featuredImageField: 'title', // Featured image attribute (e.g., 'title', 'url').
			postCustomField: '', // Custom field name for 'custom_field' field.
			postMetaKey: '', // Meta key for custom fields when 'postCustomField' is 'custom'.
			taxonomyType: '', // Taxonomy type for 'post_terms' field.
			taxonomyValueSeparator: ',', // Separator for 'post_terms' values.
			noComments: __( 'No Responses', 'spectra-pro' ), // Text for zero comments.
			oneComment: __( 'One Response', 'spectra-pro' ), // Text for one comment.
			manyComments: __( '{number} Responses', 'spectra-pro' ), // Text for multiple comments.
			authorField: '', // Author-related field for 'author' source.
			authorMetaKey: '', // Meta key for author custom fields.
			dateType: 'published', // Date type for 'post_date' (e.g., 'published', 'modified').
			dateFormat: 'default', // Date format for 'post_date'.
			customDateFormat: '', // Custom date format when 'dateFormat' is 'custom'.
			timeType: 'published', // Time type for 'post_time'.
			timeFormat: 'default', // Time format for 'post_time'.
			customTimeFormat: '', // Custom time format when 'timeFormat' is 'custom'.
			userMetaKey: '', // Meta key for 'current_user' source.
			requestParamKey: '', // Parameter key for 'request_parameter' source.
			archiveMetaKey: '', // Meta key for 'archive_meta' field.
			customArchiveMetaKey: '', // Custom meta key for 'archive_meta' when 'archiveMetaKey' is 'custom'.
		},
		replaceFullContent: false, // Flag to replace entire content with dynamic content.
		enableLink: false, // Flag to enable dynamic link generation.
		linkSource: '', // Source for dynamic link URL.
		linkSourcePostCustomField: '', // Custom field for link when 'linkSource' is 'custom_field'.
		linkSourcePostCustomMetaKey: '', // Meta key for link custom field.
		linkSourceAuthorCustomField: '', // Author field for link when 'linkSource' is 'author_info'.
		linkSourceAuthorMetaKey: '', // Meta key for author link custom field.
		advanced: {
			before: '', // Text to prepend to dynamic content.
			after: '', // Text to append to dynamic content.
			charLength: '', // Maximum character length for content.
			fallback: '', // Fallback content if dynamic content is unavailable.
		},
	};

	// State to manage dynamic content settings, initialized with initialState.
	const [ settings, setSettings ] = useState( initialState );

	/**
	 * Updates a setting at the specified path in the settings object.
	 *
	 * Supports nested paths (e.g., 'source.field') and performs a deep merge to
	 * preserve existing settings.
	 *
	 * @param {string} path  - The dot-separated path to the setting (e.g., 'source.field').
	 * @param {*}      value - The new value for the setting.
	 */
	const updateSetting = useCallback( ( path, value ) => {
		setSettings( ( prev ) => {
			const pathParts = path.split( '.' );
			// Construct a nested object from the path and value.
			const newValue = pathParts.reduceRight( ( acc, part, index ) => {
				if ( index === pathParts.length - 1 ) {
					return { [ part ]: value };
				}
				return { [ part ]: acc };
			}, {} );

			const mergeDeep = ( target, source ) => {
				const output = { ...target };
				Object.keys( source ).forEach( ( key ) => {
					if ( source[ key ] && typeof source[ key ] === 'object' && ! Array.isArray( source[ key ] ) ) {
						output[ key ] = mergeDeep( target[ key ], source[ key ] );
					} else {
						output[ key ] = source[ key ];
					}
				} );
				return output;
			};

			return mergeDeep( prev, newValue );
		} );
	}, [] );

	/**
	 * Updates an advanced setting by key.
	 *
	 * Simplifies updating fields within the 'advanced' object (e.g., 'before', 'fallback').
	 *
	 * @param {string} key   - The key within the advanced settings (e.g., 'before').
	 * @param {*}      value - The new value for the advanced setting.
	 */
	const updateAdvancedSetting = useCallback(
		( key, value ) => {
			updateSetting( `advanced.${ key }`, value );
		},
		[ updateSetting ]
	);

	/**
	 * Resets settings to their initial state.
	 */
	const resetSettings = useCallback( () => {
		setSettings( initialState );
	}, [] );

	// Context value with settings and utility functions.
	const value = {
		settings,
		updateSetting,
		updateAdvancedSetting,
		resetSettings,
	};

	// Render the provider with the context value and child components.
	return <DynamicContentContext.Provider value={ value }>{ children }</DynamicContentContext.Provider>;
};

/**
 * Custom hook to access the DynamicContentContext.
 *
 * Provides access to the dynamic content settings and utility functions for
 * components within the DynamicContentProvider.
 *
 * @return {Object} The context value containing settings and utility functions.
 */
export const useDynamicContent = () => useContext( DynamicContentContext );
