/**
 * Hit-testing utilities for canvas elements.
 *
 * Abstracts `document.elementFromPoint` / `document.elementsFromPoint`
 * behind testable functions. In tests or SSR, consumers can provide
 * a mock implementation via `setHitTestProvider`.
 */

// --- Types ---

export interface HitTestResult {
	/** The node ID found at the position, or null */
	nodeId: string | null;
	/** The raw DOM element found, or null */
	element: Element | null;
}

export interface PortHitTestResult {
	/** Node ID the port belongs to */
	nodeId: string;
	/** Port ID */
	portId: string;
}

export interface HitTestProvider {
	elementFromPoint(x: number, y: number): Element | null;
	elementsFromPoint(x: number, y: number): Element[];
}

// --- Default provider (real DOM) ---

const defaultProvider: HitTestProvider = {
	elementFromPoint: (x, y) => document.elementFromPoint(x, y),
	elementsFromPoint: (x, y) => document.elementsFromPoint(x, y),
};

let _provider: HitTestProvider = defaultProvider;

/**
 * Override the hit-test provider (for testing or SSR).
 * Pass `null` to restore the default DOM provider.
 */
export function setHitTestProvider(provider: HitTestProvider | null): void {
	_provider = provider ?? defaultProvider;
}

// --- Hit-test functions ---

/**
 * Find the canvas node at a screen position.
 * Looks for the nearest ancestor with `[data-node-id]`.
 */
export function hitTestNode(screenX: number, screenY: number): HitTestResult {
	const element = _provider.elementFromPoint(screenX, screenY);
	const nodeElement = element?.closest('[data-node-id]') ?? null;
	const nodeId = nodeElement?.getAttribute('data-node-id') ?? null;
	return { nodeId, element: nodeElement };
}

/**
 * Find a port at a screen position.
 * Scans through all elements at the position for `[data-drag-port-id]`.
 */
export function hitTestPort(screenX: number, screenY: number): PortHitTestResult | null {
	const elements = _provider.elementsFromPoint(screenX, screenY);

	for (const el of elements) {
		const portElement = el.closest('[data-drag-port-id]') as HTMLElement | null;
		if (portElement) {
			const portId = portElement.dataset.dragPortId;
			const portBar = portElement.closest('[data-port-bar]') as HTMLElement | null;
			const nodeId = portBar?.dataset.nodeId;

			if (portId && nodeId) {
				return { nodeId, portId };
			}
		}
	}

	return null;
}
