/**
 * External dependencies.
 */
import { useState, useEffect } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { BaseControl } from '@wordpress/components';
import AsyncSelect from 'react-select/async';
import { decodeEntities } from '@wordpress/html-entities';

/**
 * Renders an asynchronous select control for choosing a post.
 *
 * Displays a searchable dropdown for selecting a post when the source is 'post_type'.
 * Fetches initial posts and supports searching for additional posts, updating the dynamic
 * content settings with the selected post ID.
 *
 * @since x.x.x
 *
 * @param {Object}   props               - The component props.
 * @param {Object}   props.settings      - The dynamic content settings from the context.
 * @param {Function} props.updateSetting - Function to update settings in the context.
 * @return {Element|null} The post selector control or null if source is not 'post_type'.
 */
const PostSelector = ( { settings, updateSetting } ) => {
	// State for the selected post.
	const [ selectedPost, setSelectedPost ] = useState( null );

	// Get the post type from settings, default to 'post'.
	const postType = settings.source.postType || 'post';

	// Fetch initial 5 latest posts and check loading status.
	const { initialOptions, isLoadingInitial } = useSelect(
		( select ) => {
			if ( settings.source.type !== 'post_type' ) {
				return { initialOptions: [], isLoadingInitial: false };
			}

			const query = { per_page: 5, orderby: 'date', order: 'desc', post_type: postType };
			const posts = select( 'core' ).getEntityRecords( 'postType', postType, query );
			const hasResolved = select( 'core' ).hasFinishedResolution( 'getEntityRecords', [
				'postType',
				postType,
				query,
			] );

			return {
				initialOptions: posts
					? posts.map( ( post ) => ( {
							value: post.id.toString(),
							label: decodeEntities( post.title.rendered ) || __( 'Untitled', 'spectra-pro' ),
					  } ) )
					: [],
				isLoadingInitial: ! hasResolved,
			};
		},
		[ settings.source.type, postType ]
	);

	// Fetch selected post details.
	const selectedPostOption = useSelect(
		( select ) => {
			if ( settings.source.type !== 'post_type' || ! settings.source.postId ) return null;

			const post = select( 'core' ).getEntityRecord( 'postType', postType, settings.source.postId );
			return post
				? {
						value: post.id.toString(),
						label: decodeEntities( post.title.rendered ) || __( 'Untitled', 'spectra-pro' ),
				  }
				: null;
		},
		[ settings.source.type, settings.source.postId, postType ]
	);

	// Update selected post when it changes or when post type changes.
	useEffect( () => {
		if ( selectedPostOption && selectedPostOption.value === settings.source.postId?.toString() ) {
			setSelectedPost( selectedPostOption );
		} else {
			// Reset if post type changed or post doesn't match.
			setSelectedPost( null );
		}
	}, [ selectedPostOption, postType, settings.source.postId ] );

	// Get the post type object to get proper labels.
	const postTypeObject = useSelect(
		( select ) => {
			if ( ! postType ) return null;
			return select( 'core' ).getPostType( postType );
		},
		[ postType ]
	);

	/**
	 * Loads post options based on search input.
	 *
	 * @param {string}   inputValue - The search input value.
	 * @param {Function} callback   - Callback to return the options.
	 */
	const loadOptions = ( inputValue, callback ) => {
		const query = inputValue.trim()
			? { search: inputValue, per_page: 10, post_type: postType }
			: { per_page: 5, orderby: 'date', order: 'desc', post_type: postType };

		const posts = wp.data.select( 'core' ).getEntityRecords( 'postType', postType, query );
		const hasResolved = wp.data
			.select( 'core' )
			.hasFinishedResolution( 'getEntityRecords', [ 'postType', postType, query ] );

		if ( posts ) {
			const options = posts.map( ( post ) => ( {
				value: post.id.toString(),
				label: decodeEntities( post.title.rendered ) || __( 'Untitled', 'spectra-pro' ),
			} ) );
			callback( options );
		} else if ( ! hasResolved ) {
			callback( [] ); // Show loading state.
		}
	};

	// Return null if source is not 'post_type'.
	if ( settings.source.type !== 'post_type' ) return null;

	// Custom styles to match Gutenberg controls behavior.
	const customStyles = {
		control: ( provided, state ) => ( {
			...provided,
			cursor: state.menuIsOpen ? 'default' : 'pointer',
			color: 'currentcolor',
			fontFamily: 'inherit',
			border: '1px solid var(--wp-components-color-gray-600, #949494)',
		} ),
		clearIndicator: ( provided ) => ( {
			...provided,
			':hover': {
				color: provided.color,
				cursor: 'pointer',
			},
		} ),
	};

	// Get post type label.
	const postTypeLabel = postTypeObject?.labels?.singular_name
		? sprintf(
				/* translators: %s: post type singular name */
				__( 'Select %s', 'spectra-pro' ),
				decodeEntities( postTypeObject.labels.singular_name )
		  )
		: __( 'Select Post', 'spectra-pro' );

	return (
		<BaseControl id="spectra-pro-dynamic-content-post-selector" label={ postTypeLabel }>
			<AsyncSelect
				key={ postType } // Force re-render when post type changes
				cacheOptions
				loadOptions={ loadOptions }
				defaultOptions={ initialOptions }
				value={ selectedPost }
				onChange={ ( selectedOption ) => {
					setSelectedPost( selectedOption );
					updateSetting( 'source.postId', selectedOption ? selectedOption.value : 0 );
				} }
				placeholder={
					isLoadingInitial ? __( 'Loading…', 'spectra-pro' ) : __( 'Search for a post…', 'spectra-pro' )
				}
				noOptionsMessage={ () => __( 'No posts found', 'spectra-pro' ) }
				loadingMessage={ () => __( 'Loading posts…', 'spectra-pro' ) }
				isClearable
				isDisabled={ isLoadingInitial }
				styles={ customStyles }
			/>
		</BaseControl>
	);
};

export default PostSelector;
