/**
 * Generic hook for fetching data from a REST API endpoint.
 *
 * @package RawTypeOE
 */

import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
import apiFetch from '../utils/apiFetch';

// Module-level response cache. Big-store endpoints (categories tree, product
// tags, statuses) can take seconds to resolve; reusing the response across
// re-mounts of filter chips keeps the wizard snappy.
const responseCache = new Map();
const inflightCache = new Map();

export function clearApiDataCache( path ) {
	if ( path ) {
		responseCache.delete( path );
		inflightCache.delete( path );
		return;
	}

	responseCache.clear();
	inflightCache.clear();
}

/**
 * Fetch data from a REST endpoint with automatic AbortController cleanup,
 * dedup of concurrent requests for the same path and a module-level cache so
 * heavy lookups (categories, tags, statuses) only hit the server once.
 *
 * @param {string|null}  path              REST path to fetch.
 * @param {Object}       options           Hook options.
 * @param {boolean}      options.enabled   Whether fetching is enabled (default true).
 * @param {Function}     options.transform Optional transform applied to response data.
 * @return {{ data: Array, loading: boolean, error: Error|null, refetch: Function }}
 */
export default function useApiData( path, { enabled = true, transform = null } = {} ) {
	const cachedInitial = path && responseCache.has( path ) ? responseCache.get( path ) : [];
	const [ data, setData ] = useState( cachedInitial );
	const [ loading, setLoading ] = useState( ! ( path && responseCache.has( path ) ) );
	const [ error, setError ] = useState( null );
	const abortRef = useRef( null );

	const fetchData = useCallback(
		( { force = false } = {} ) => {
			if ( ! enabled || ! path ) {
				setLoading( false );
				return;
			}

			if ( ! force && responseCache.has( path ) ) {
				const cached = responseCache.get( path );
				setData( cached );
				setLoading( false );
				return;
			}

			if ( abortRef.current ) {
				abortRef.current.abort();
			}
			const controller = new AbortController();
			abortRef.current = controller;

			setLoading( true );
			setError( null );

			let pending = inflightCache.get( path );
			if ( ! pending ) {
				pending = apiFetch( { path, signal: controller.signal } )
					.then( ( response ) => {
						inflightCache.delete( path );
						responseCache.set( path, response );
						return response;
					} )
					.catch( ( err ) => {
						inflightCache.delete( path );
						throw err;
					} );
				inflightCache.set( path, pending );
			}

			pending
				.then( ( response ) => {
					const result = transform
						? transform( response )
						: ( Array.isArray( response ) ? response : [] );
					setData( result );
					setLoading( false );
				} )
				.catch( ( err ) => {
					if ( 'AbortError' !== err.name ) {
						setError( err );
						setLoading( false );
					}
				} );
		},
		[ path, enabled, transform ],
	);

	useEffect( () => {
		fetchData();
		return () => {
			if ( abortRef.current ) {
				abortRef.current.abort();
			}
		};
	}, [ fetchData ] );

	return {
		data,
		loading,
		error,
		refetch: ( opts ) => fetchData( { force: true, ...( opts || {} ) } ),
	};
}
