import {useState, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle, useRef} from '@wordpress/element';
import {Spinner} from '@wordpress/components';
import {__} from '@wordpress/i18n';
import SourceColumn from '../components/AssignmentSourceColumn';
import TargetColumn from '../components/AssignmentTargetColumn';
import './AssignmentField.scss';
import '../scss/_assignment.scss';

const {apiNonce} = window.adpressoAdminData || {};
const assignmentCache = {};

export const clearAssignmentCacheForItem = ( itemId ) => {
	if ( itemId && assignmentCache[itemId] ) {
		delete assignmentCache[itemId];
		console.log( `Cache cleared for item #${itemId}` );
	}
};
const AssignmentField = forwardRef( ( {field, value, onChange, entityData, onSetDirty}, ref ) => {
	const currentAdId = entityData.id;
	const restUrl = 'adpresso/v1/';

	// --- State Management ---
	const [groups, setGroups] = useState( [] );
	const [placements, setPlacements] = useState( [] );
	const [activeContext, setActiveContext] = useState( null );
	const [assignments, setAssignments] = useState( {groupIds: [], placementIds: []} );
	const [isLoading, setIsLoading] = useState( true );
	const [isTargetLoading, setIsTargetLoading] = useState( false );
	const [localChanges, setLocalChanges] = useState( [] );
	const [groupSearch, setGroupSearch] = useState( '' );
	const [placementSearch, setPlacementSearch] = useState( '' );
	const hasBecomeDirty = useRef( false );

	// Hook for initial loading of the groups.
	useEffect( () => {
		setIsLoading( true );
		const endpoint = `${restUrl}assignments/ad/${currentAdId}`;

		if ( assignmentCache[currentAdId] ) {
			const data = assignmentCache[currentAdId];
			console.log( `Loading assignments for Ad #${currentAdId} from cache.` );

			setGroups( data.groups || [] );
			setPlacements( data.placements || [] );
			const assignedGroupIds = (data.groups || []).filter( g => g.is_assigned_to_ad ).map( g => g.id );
			setAssignments( {
				groupIds:     assignedGroupIds,
				placementIds: data.singular_ad_assignments?.placement_ids || []
			} );
			setIsLoading( false );
			return;
		}

		wp.apiFetch( {path: endpoint, headers: {'X-WP-Nonce': apiNonce}} )
		  .then( data => {
			  assignmentCache[currentAdId] = data;

			  // The data is already sorted from PHP
			  setGroups( data.groups || [] );
			  setPlacements( data.placements || [] );

			  // Derive the initially assigned group IDs from the pre-processed data
			  const assignedGroupIds = (data.groups || [])
				  .filter( group => group.is_assigned_to_ad )
				  .map( group => group.id );

			  // Set the initial assignments for both contexts
			  setAssignments( {
				  groupIds:     assignedGroupIds,
				  placementIds: data.singular_ad_assignments?.placement_ids || []
			  } );
		  } )
		  .catch( error => console.error( 'Failed to load initial assignment data:', error ) )
		  .finally( () => setIsLoading( false ) );
	}, [currentAdId] );

	const filteredGroups = useMemo( () => {
		if ( !groupSearch ) {
			return groups;
		}
		const lowerCaseQuery = groupSearch.toLowerCase();
		return groups.filter( group =>
			group.title.toLowerCase().includes( lowerCaseQuery )
		);
	}, [groups, groupSearch] );

	const filteredPlacements = useMemo( () => {
		if ( !placementSearch ) {
			return placements;
		}
		const lowerCaseQuery = placementSearch.toLowerCase();
		return placements.filter( placement =>
			placement.title.toLowerCase().includes( lowerCaseQuery ) ||
			placement.type_label.toLowerCase().includes( lowerCaseQuery )
		);
	}, [placements, placementSearch] );

	const handleContextChange = useCallback( ( newContext ) => {
		if ( activeContext && newContext.type === activeContext.type && newContext.id === activeContext.id ) {
			return;
		}
		setIsTargetLoading( true );
		setActiveContext( newContext );

		const isSingular = newContext.type === 'singular';
		const endpoint = isSingular
			? `${restUrl}assignments/ad/${newContext.id}`
			: `${restUrl}assignments/group/${newContext.id}`;

		wp.apiFetch( {path: endpoint, headers: {'X-WP-Nonce': apiNonce}} )
		  .then( data => {
			  if ( data.placements ) {
				  setPlacements( data.placements );
			  }

			  // Use the right key based on the context.
			  const newPlacementIds = isSingular
				  ? data.singular_ad_assignments?.placement_ids || []
				  : data.assigned_placement_ids || [];

			  setAssignments( prev => ({
				  ...prev,
				  placementIds: newPlacementIds
			  }) );
		  } )
		  .catch( error => {
			  console.error( `Failed to load assignment data for context:`, newContext, error );
			  setPlacements( [] );
			  setAssignments( prev => ({...prev, placementIds: []}) );
		  } )
		  .finally( () => {
			  setIsTargetLoading( false );
		  } );
	}, [activeContext] );

	useImperativeHandle( ref, () => ({
		getChanges: () => {
			// Return the gathered changes and empty the list for the next save progress.
			const changes = [...localChanges];
			setLocalChanges( [] );
			return changes;
		}
	}) );

	const handleAssignmentChange = useCallback( ( change ) => {
		if ( change.action.includes( 'group' ) ) {
			setAssignments( prev => {
				const newGroupIds = new Set( prev.groupIds );
				if ( change.action === 'add_to_group' ) {
					newGroupIds.add( change.groupId );
				} else {
					newGroupIds.delete( change.groupId );
				}
				return {...prev, groupIds: Array.from( newGroupIds )};
			} );
		} else if ( change.action.includes( 'placement' ) ) {
			setAssignments( prev => {
				const newPlacementIds = new Set( prev.placementIds );
				if ( change.action === 'assign_to_placement' ) {
					newPlacementIds.add( change.placementId );
				} else {
					newPlacementIds.delete( change.placementId );
				}
				return {...prev, placementIds: Array.from( newPlacementIds )};
			} );
		}

		if ( !hasBecomeDirty.current ) {
			onSetDirty();
			hasBecomeDirty.current = true;
		}

		setLocalChanges( prevChanges => {
			return [...prevChanges, {...change, context: activeContext}];
		} );
	}, [activeContext, onSetDirty] );

	if ( isLoading ) {
		return <Spinner/>;
	}

	return (
		<div className="adpresso-assignment-field">
			<span>{__( 'Assign without grouping', 'adpresso' )}</span>
			<span>{__( 'Assign the selected item to a placement', 'adpresso' )}</span>
			<SourceColumn
				adId={currentAdId}
				groups={filteredGroups}
				onSearchChange={setGroupSearch}
				searchQuery={groupSearch}
				activeContext={activeContext}
				onContextChange={handleContextChange}
				assignments={assignments}
				onAssignmentChange={handleAssignmentChange}
			/>

			<TargetColumn
				placements={filteredPlacements}
				onSearchChange={setPlacementSearch}
				searchQuery={placementSearch}
				activeContext={activeContext}
				assignments={assignments}
				onAssignmentChange={handleAssignmentChange}
				isTargetLoading={isTargetLoading}
			/>
		</div>
	);
} );

export default AssignmentField;
