/**
 * Canvas Component
 *
 * Main orchestrating component that combines Viewport, Nodes, and Edges.
 * Uses the v2 gesture pipeline — all gesture routing flows through
 * InputProvider → useCanvasGestures / useNodeGestures.
 */

import React, { type ErrorInfo } from 'react';
import { Viewport, type ViewportProps } from './Viewport';
import { NodeRenderer, type NodeRendererProps } from './NodeRenderer';
import { EdgeRenderer, type EdgeRendererProps } from './EdgeRenderer';
import { EdgeLabelEditor } from './EdgeLabelEditor';
import { EdgePreviewLine } from './EdgePreviewLine';
import type { NodeId, UINodeState, UINodeProperties } from '../core/types';
import { useSelectionChangeEffect } from '../hooks/useSelectionChangeEffect';
import { useViewportChangeEffect } from '../hooks/useViewportChangeEffect';
import { useDragLifecycleEffect } from '../hooks/useDragLifecycleEffect';
import { InputProvider, type InputConfig } from '../gestures/GestureProvider';
import type { BivariantCallback, InputEvent, ResolvedAction } from '../gestures/types';

export interface CanvasProps extends Omit<ViewportProps, 'children'> {
	/**
	 * Render function for custom node content.
	 * When omitted, the node type registry is used to auto-resolve components
	 * by `node.dbData.node_type` (including those registered by plugins).
	 */
	renderNode?: NodeRendererProps['renderNode'];

	// --- Edge event callbacks ---
	/** Callback when an edge is clicked */
	onEdgeClick?: EdgeRendererProps['onEdgeClick'];
	/** Callback when an edge is double-clicked */
	onEdgeDoubleClick?: EdgeRendererProps['onEdgeDoubleClick'];
	/** Callback when an edge is right-clicked */
	onEdgeRightClick?: EdgeRendererProps['onEdgeRightClick'];
	/** Callback when pointer enters an edge */
	onEdgeHover?: EdgeRendererProps['onEdgeHover'];
	/** Callback when pointer leaves an edge */
	onEdgeLeave?: EdgeRendererProps['onEdgeLeave'];

	// --- Observability callbacks ---
	/** Callback when selection changes (node or edge selection) */
	onSelectionChange?: (selectedNodeIds: Set<string>, selectedEdgeId: string | null) => void;
	/** Callback when viewport changes (zoom or pan) */
	onViewportChange?: (viewport: { zoom: number; pan: { x: number; y: number } }) => void;
	/** Callback when a node drag starts */
	onDragStart?: (nodeIds: string[]) => void;
	/** Callback when a node drag ends */
	onDragEnd?: (nodeIds: string[], positions: Map<string, { x: number; y: number }>) => void;

	// --- Persistence callbacks ---
	/** Callback to persist node changes */
	onNodePersist?: (nodeId: NodeId, graphId: string, uiProperties: UINodeProperties) => Promise<void>;
	/** Callback when persist fails */
	onNodePersistError?: (nodeId: NodeId, error: Error) => void;

	// --- Error callbacks ---
	/** Callback when a node fails to render */
	onNodeRenderError?: (nodeId: NodeId, error: Error, errorInfo: ErrorInfo) => void;
	/** Custom fallback UI for errored nodes */
	renderErrorFallback?: (props: { nodeId: string; error: Error; reset: () => void }) => React.ReactNode;

	// --- Gesture system v2 ---
	/** Configuration for the shared input pipeline (contexts, palm rejection) */
	gestureConfig?: InputConfig;
	/** Unified input callback — fires for every resolved pointer or keyboard action */
	onAction?: BivariantCallback<[InputEvent, ResolvedAction]>;

	// --- Layout ---
	/** Optional wrapper component around nodes */
	nodeWrapper?: React.ComponentType<{ children: React.ReactNode; nodeData: UINodeState }>;
	/** Additional content to render inside the viewport (overlays, controls, etc.) */
	children?: React.ReactNode;
}

export function Canvas({
	renderNode,
	onEdgeClick,
	onEdgeDoubleClick,
	onEdgeRightClick,
	onEdgeHover,
	onEdgeLeave,
	onSelectionChange,
	onViewportChange,
	onDragStart,
	onDragEnd,
	onNodePersist,
	onNodePersistError,
	onNodeRenderError,
	renderErrorFallback,
	gestureConfig,
	onAction,
	nodeWrapper,
	children,
	...viewportProps
}: CanvasProps) {
	// Observability hooks
	useSelectionChangeEffect(onSelectionChange);
	useViewportChangeEffect(onViewportChange);
	useDragLifecycleEffect(onDragStart, onDragEnd);

	return (
		<InputProvider gestureConfig={gestureConfig} onAction={onAction}>
			<Viewport {...viewportProps}>
				<EdgeRenderer
					onEdgeClick={onEdgeClick}
					onEdgeDoubleClick={onEdgeDoubleClick}
					onEdgeRightClick={onEdgeRightClick}
					onEdgeHover={onEdgeHover}
					onEdgeLeave={onEdgeLeave}
				/>
				<EdgePreviewLine />
				<NodeRenderer
					renderNode={renderNode}
					onNodePersist={onNodePersist}
					onNodePersistError={onNodePersistError}
					onNodeRenderError={onNodeRenderError}
					renderErrorFallback={renderErrorFallback}
					nodeWrapper={nodeWrapper}
				/>
				<EdgeLabelEditor />
				{children}
			</Viewport>
		</InputProvider>
	);
}
