/**
 * WordPress dependencies
 */
import { useSelect } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
import { store as coreStore } from '@wordpress/core-data';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { decodeEntities } from '@wordpress/html-entities';
import { __ } from '@wordpress/i18n';
import {
	cloneBlock,
	getBlockSupport,
	store as blocksStore,
} from '@wordpress/blocks';

/** @typedef {import('@wordpress/blocks').WPBlockVariation} WPBlockVariation */

/**
 * @typedef IHasNameAndId
 * @property {string|number} id   The entity's id.
 * @property {string}        name The entity's name.
 */

/**
 * The object used in User Directory block that contains info and helper mappings
 * from an array of IHasNameAndId objects.
 *
 * @typedef {Object} UserDirectoryEntitiesInfo
 * @property {IHasNameAndId[]}               entities  The array of entities.
 * @property {Object<string, IHasNameAndId>} mapById   Object mapping with the id as key and the entity as value.
 * @property {Object<string, IHasNameAndId>} mapByName Object mapping with the name as key and the entity as value.
 * @property {string[]}                      names     Array with the entities' names.
 */

/**
 * Returns a helper object with mapping from Objects that implement
 * the `IHasNameAndId` interface. The returned object is used for
 * integration with `FormTokenField` component.
 *
 * @param {IHasNameAndId[]} entities The entities to extract of helper object.
 * @return {UserDirectoryEntitiesInfo} The object with the entities information.
 */
export const getEntitiesInfo = (entities) => {
	const mapping = entities?.reduce(
		(accumulator, entity) => {
			const { mapById, mapByName, names } = accumulator;
			mapById[entity.id] = entity;
			mapByName[entity.name] = entity;
			names.push(entity.name);
			return accumulator;
		},
		{ mapById: {}, mapByName: {}, names: [] }
	);
	return {
		entities,
		...mapping,
	};
};

/**
 * Helper util to return a value from a certain path of the object.
 * Path is specified as a string of properties, separated by dots,
 * for example: "parent.child".
 *
 * @param {Object} object Input object.
 * @param {string} path   Path to the object property.
 * @return {*} Value of the object property at the specified path.
 */
export const getValueFromObjectPath = (object, path) => {
	const normalizedPath = path.split('.');
	let value = object;
	normalizedPath.forEach((fieldName) => {
		value = value?.[fieldName];
	});
	return value;
};

/**
 * Helper util to map records to add a `name` prop from a
 * provided path, in order to handle all entities in the same
 * fashion(implementing`IHasNameAndId` interface).
 *
 * @param {Object[]} entities The array of entities.
 * @param {string}   path     The path to map a `name` property from the entity.
 * @return {IHasNameAndId[]} An array of entities that now implement the `IHasNameAndId` interface.
 */
export const mapToIHasNameAndId = (entities, path) => {
	return (entities || []).map((entity) => ({
		...entity,
		name: decodeEntities(getValueFromObjectPath(entity, path)),
	}));
};

/**
 * Returns a helper object that contains:
 * 1. An `options` object from the available user roles, to be passed to a `SelectControl`.
 * 2. A helper map with available user capabilities per role.
 *
 * @return {Object} The helper object related to user roles.
 */
export const useUserRoles = () => {
	const userRoles = useSelect((select) => {
		const { getEntityRecords } = select(coreStore);
		const roles = getEntityRecords('root', 'user-roles');
		return roles || [];
	}, []);
	
	const userRolesSelectOptions = useMemo(
		() =>
			(userRoles || []).map(({ name, label }) => ({
				label: label,
				value: name,
			})),
		[userRoles]
	);
	
	return {
		userRolesSelectOptions,
	};
};

/**
 * List of available options to order users by.
 *
 * @return {Array} List of order options.
 */
export function useUserOrderByOptions() {
	return [
		{ label: __('ID', 'wpuf-pro'), value: 'id' },
		{ label: __('Username', 'wpuf-pro'), value: 'user_login' },
		{ label: __('Display Name', 'wpuf-pro'), value: 'display_name' },
		{ label: __('Email', 'wpuf-pro'), value: 'user_email' },
		{ label: __('Registration Date', 'wpuf-pro'), value: 'user_registered' },
	];
}

/**
 * List of available searchable fields for users.
 *
 * @return {Array} List of searchable fields.
 */
export function useSearchableFields() {
	return [
		{ label: __('Username', 'wpuf-pro'), value: 'user_login' },
		{ label: __('Display Name', 'wpuf-pro'), value: 'display_name' },
		{ label: __('Email', 'wpuf-pro'), value: 'user_email' },
		{ label: __('First Name', 'wpuf-pro'), value: 'first_name' },
		{ label: __('Last Name', 'wpuf-pro'), value: 'last_name' },
	];
}

/**
 * Transforms blocks from a pattern to work with User Directory block.
 * This is similar to Query Loop's getTransformedBlocksFromPattern but adapted for users.
 *
 * @param {Array} blocks The blocks to transform.
 * @param {Object} userDirectoryAttributes The User Directory block attributes.
 * @return {Object} The transformed blocks and query client IDs.
 */
export const getTransformedBlocksFromPattern = (
	blocks,
	userDirectoryAttributes
) => {
	const transformedBlocks = blocks.map((block) => {
		// Clone the block to avoid mutations
		const clonedBlock = cloneBlock(block);
		
		// If this is a user-directory-item block, we need to transform it
		if (clonedBlock.name === 'wpuf-ud/directory-item') {
			// Add the user directory context to the block
			clonedBlock.attributes = {
				...clonedBlock.attributes,
				...userDirectoryAttributes,
			};
		}
		
		// Recursively transform inner blocks
		if (clonedBlock.innerBlocks) {
			clonedBlock.innerBlocks = getTransformedBlocksFromPattern(
				clonedBlock.innerBlocks,
				userDirectoryAttributes
			).newBlocks;
		}
		
		return clonedBlock;
	});
	
	return {
		newBlocks: transformedBlocks,
		queryClientIds: [userDirectoryAttributes.clientId],
	};
};

/**
 * Hook that returns the block name for patterns.
 * For User Directory, we use 'wpuf-ud/directory-item' as the pattern block.
 *
 * @param {string} clientId The block client ID.
 * @param {Object} attributes The block attributes.
 * @return {string} The block name for patterns.
 */
export function useBlockNameForPatterns(clientId, attributes) {
	return 'wpuf-ud/directory-item';
}

/**
 * Hook that returns scoped block variations for User Directory.
 * This filters variations based on the current block attributes.
 *
 * @param {Object} attributes The block attributes.
 * @return {WPBlockVariation[]} The scoped block variations.
 */
export function useScopedBlockVariations(attributes) {
	const { query, roles } = attributes;
	
	return useSelect(
		(select) => {
			const { getBlockVariations } = select(blocksStore);
			const variations = getBlockVariations('wpuf-ud/directory');
			
			if (!variations) {
				return [];
			}
			
			// Filter variations based on query attributes
			return variations.filter((variation) => {
				// If variation has specific role requirements, check them
				if (variation.attributes?.roles && roles !== 'all') {
					return variation.attributes.roles === roles;
				}
				
				return true;
			});
		},
		[query, roles]
	);
}

/**
 * Hook that returns patterns for User Directory block.
 *
 * @param {string} clientId The block client ID.
 * @param {string} name The block name.
 * @return {Array} The patterns for the block.
 */
export const usePatterns = (clientId, name) => {
	return useSelect(
		(select) => {
			// Check if getBlockPatterns exists before calling it
			const blocksStoreSelect = select(blocksStore);
			if (!blocksStoreSelect || typeof blocksStoreSelect.getBlockPatterns !== 'function') {
				return [];
			}
			
			const patterns = blocksStoreSelect.getBlockPatterns(name);
			
			if (!patterns) {
				return [];
			}
			
			// Filter patterns that are compatible with User Directory
			return patterns.filter((pattern) => {
						// Check if pattern contains user-directory-item blocks
		const hasUserDirectoryItem = pattern.blocks.some(
			(block) => block.name === 'wpuf-ud/directory-item'
		);
				
				return hasUserDirectoryItem;
			});
		},
		[clientId, name]
	);
};

/**
 * Hook that returns unsupported blocks in User Directory.
 *
 * @param {string} clientId The block client ID.
 * @return {Array} The unsupported blocks.
 */
export const useUnsupportedBlocks = (clientId) => {
	return useSelect(
		(select) => {
			const { getBlocks } = select(blockEditorStore);
			const blocks = getBlocks(clientId);
			
			if (!blocks) {
				return [];
			}
			
					// Define supported block types for User Directory
		const supportedBlocks = [
			'wpuf-ud/directory-item',
			'wpuf-ud/avatar',
			'wpuf-ud/name',
			'wpuf-ud/bio',
			'wpuf-ud/contact',
			'wpuf-ud/custom-field',
			'wpuf-ud/posts',
			'wpuf-ud/comments',
			'core/group',
			'core/button',
			'core/paragraph',
			'core/heading',
		];
			
			// Recursively check for unsupported blocks
			const findUnsupportedBlocks = (blockList) => {
				const unsupported = [];
				
				blockList.forEach((block) => {
					if (!supportedBlocks.includes(block.name)) {
						unsupported.push(block);
					}
					
					if (block.innerBlocks) {
						unsupported.push(...findUnsupportedBlocks(block.innerBlocks));
					}
				});
				
				return unsupported;
			};
			
			return findUnsupportedBlocks(blocks);
		},
		[clientId]
	);
}; 