import { useEffect, useMemo, useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import {
	buildBatchSummary,
	buildBulkPurchaseOrders,
	findLargestAddressGroup,
} from './display';
import { ORDERS_SHIPPING_CONTEXT_ENTITY } from './constants';
import { toError } from './store';
import { useAutoAssignedPackages } from './use-auto-assigned-packages';
import type {
	AddressGrouping,
	BatchSummary,
	BulkPurchaseOrder,
	ManualPackageSelections,
	OrderShippingContextRecord,
} from './types';

const EMPTY_MANUAL_SELECTIONS: ManualPackageSelections = {};

interface UseBulkPurchaseOrdersResult {
	isResolving: boolean;
	error: Error | null;
	records: OrderShippingContextRecord[];
	orders: BulkPurchaseOrder[];
	summary: BatchSummary;
	grouping: AddressGrouping | null;
}

const EMPTY_RECORDS: OrderShippingContextRecord[] = [];

/**
 * Reactive hook that pulls orders shipping context via core-data's
 * useEntityRecords, then layers on the WOOSHIP-2133 fields the
 * endpoint doesn't yet provide (service, cost, status, note) so the
 * modal can render every column. Components reading this hook won't
 * have to change once those fields land for real.
 */
export const useBulkPurchaseOrders = (
	orderIds: number[],
	manualSelections: ManualPackageSelections = EMPTY_MANUAL_SELECTIONS
): UseBulkPurchaseOrdersResult => {
	// Stringify so a fresh array reference with the same IDs doesn't
	// re-trigger the fetch.
	const idsKey = orderIds.join( ',' );
	const hasIds = orderIds.length > 0;

	const [ records, setRecords ] =
		useState< OrderShippingContextRecord[] >( EMPTY_RECORDS );
	const [ isShippingContextResolving, setIsShippingContextResolving ] =
		useState( false );
	const [ error, setError ] = useState< Error | null >( null );

	useEffect( () => {
		if ( ! hasIds ) {
			setRecords( EMPTY_RECORDS );
			setError( null );
			setIsShippingContextResolving( false );
			return;
		}

		let isCurrent = true;
		setRecords( EMPTY_RECORDS );
		setError( null );
		setIsShippingContextResolving( true );

		void apiFetch< OrderShippingContextRecord[] >( {
			path: addQueryArgs( ORDERS_SHIPPING_CONTEXT_ENTITY.baseURL, {
				ids: orderIds,
			} ),
		} )
			.then( ( nextRecords ) => {
				if ( ! isCurrent ) {
					return;
				}
				setRecords(
					Array.isArray( nextRecords ) ? nextRecords : EMPTY_RECORDS
				);
			} )
			.catch( ( fetchError ) => {
				if ( ! isCurrent ) {
					return;
				}
				setError( toError( fetchError ) );
			} )
			.finally( () => {
				if ( isCurrent ) {
					setIsShippingContextResolving( false );
				}
			} );

		return () => {
			isCurrent = false;
		};
		// eslint-disable-next-line react-hooks/exhaustive-deps -- idsKey covers orderIds contents.
	}, [ idsKey, hasIds ] );

	const safeRecords = useMemo( () => records ?? EMPTY_RECORDS, [ records ] );
	const usableRecords = useMemo(
		() => safeRecords.filter( ( record ) => ! record.error ),
		[ safeRecords ]
	);

	// Only ask the box-packer for orders that actually loaded — orders that
	// errored out of the shipping-context fetch are skipped entirely so we
	// don't waste a batch slot on rows the modal will never render.
	const assignableOrderIds = useMemo(
		() => usableRecords.map( ( record ) => record.order_id ),
		[ usableRecords ]
	);

	const {
		isResolving: isAutoAssignResolving,
		results: autoAssignedPackages,
		error: autoAssignError,
	} = useAutoAssignedPackages( assignableOrderIds );

	const orders = useMemo(
		() =>
			buildBulkPurchaseOrders(
				usableRecords,
				autoAssignedPackages,
				manualSelections
			),
		[ usableRecords, autoAssignedPackages, manualSelections ]
	);
	const summary = useMemo( () => buildBatchSummary( orders ), [ orders ] );
	const grouping = useMemo(
		() => findLargestAddressGroup( usableRecords ),
		[ usableRecords ]
	);

	return {
		// Hold the modal in its loading state until both the shipping-context
		// fetch and the box-packer auto-assign call have settled, so the
		// table doesn't flash placeholder packages before the suggestion
		// arrives.
		isResolving:
			hasIds && ( isShippingContextResolving || isAutoAssignResolving ),
		// Surface a failed auto-assign as a modal-level error rather than
		// letting rows fall back to placeholder packages and read as
		// "ready". The shipping-context error takes precedence since
		// without records there's nothing to suggest packages for.
		error: error ?? autoAssignError,
		records: safeRecords,
		orders,
		summary,
		grouping,
	};
};