/**
 * Headless Canvas API
 *
 * A pure Jotai store-based API for controlling the canvas without React.
 * Satisfies Principle 6: "Fully controllable headless API".
 *
 * @example
 * ```ts
 * import { createStore } from 'jotai';
 * import { createCanvasAPI } from '@blinksgg/canvas/core';
 *
 * const store = createStore();
 * const api = createCanvasAPI(store);
 *
 * api.selectNode('node-1');
 * api.fitToBounds('graph', 20);
 * api.undo();
 * ```
 */

import type { createStore } from 'jotai';
import type { ActionContext, CanvasEventType } from './settings-types';
import { executeAction, buildActionHelpers, type BuildActionHelpersOptions, type ActionExecutionResult } from './action-executor';
import { exportGraph, importGraph, validateSnapshot, type CanvasSnapshot, type ImportOptions, type ValidationResult } from './canvas-serializer';
import { eventMappingsAtom, getActionForEvent } from './settings-store';
import {
	selectedNodeIdsAtom,
	selectSingleNodeAtom,
	addNodesToSelectionAtom,
	clearSelectionAtom,
	selectEdgeAtom,
	clearEdgeSelectionAtom,
	selectedEdgeIdAtom,
} from './selection-store';
import { zoomAtom, panAtom, resetViewportAtom, fitToBoundsAtom, centerOnNodeAtom, viewportRectAtom } from './viewport-store';
import { graphAtom } from './graph-store';
import { nodeKeysAtom, edgeKeysAtom } from './graph-derived';
import { addNodeToLocalGraphAtom, optimisticDeleteNodeAtom, optimisticDeleteEdgeAtom } from './graph-mutations';
import { addEdgeToLocalGraphAtom } from './graph-mutations-edges';
import { undoAtom, redoAtom, canUndoAtom, canRedoAtom, pushHistoryAtom, clearHistoryAtom } from './history-store';
import { copyToClipboardAtom, cutToClipboardAtom, pasteFromClipboardAtom, duplicateSelectionAtom, clipboardAtom } from './clipboard-store';
import { snapEnabledAtom, snapGridSizeAtom, toggleSnapAtom } from './snap-store';
import { virtualizationEnabledAtom, visibleNodeKeysAtom, visibleEdgeKeysAtom } from './virtualization-store';
import { FitToBoundsMode } from '../utils/layout';
import type { DBGraphNode, DBGraphEdge, GraphNodeAttributes, PanOffset } from './types';

type Store = ReturnType<typeof createStore>;

export interface CanvasAPI {
	// --- Selection ---
	selectNode: (id: string) => void;
	addToSelection: (ids: string[]) => void;
	clearSelection: () => void;
	getSelectedNodeIds: () => string[];
	selectEdge: (edgeId: string) => void;
	clearEdgeSelection: () => void;
	getSelectedEdgeId: () => string | null;

	// --- Viewport ---
	getZoom: () => number;
	setZoom: (zoom: number) => void;
	getPan: () => PanOffset;
	setPan: (pan: PanOffset) => void;
	resetViewport: () => void;
	fitToBounds: (mode: 'graph' | 'selection', padding?: number) => void;
	centerOnNode: (nodeId: string) => void;

	// --- Graph ---
	addNode: (node: DBGraphNode) => void;
	removeNode: (nodeId: string) => void;
	addEdge: (edge: DBGraphEdge) => void;
	removeEdge: (edgeKey: string) => void;
	getNodeKeys: () => string[];
	getEdgeKeys: () => string[];
	getNodeAttributes: (id: string) => GraphNodeAttributes | undefined;

	// --- History ---
	undo: () => void;
	redo: () => void;
	canUndo: () => boolean;
	canRedo: () => boolean;
	recordSnapshot: (label?: string) => void;
	clearHistory: () => void;

	// --- Clipboard ---
	copy: () => void;
	cut: () => void;
	paste: () => void;
	duplicate: () => void;
	hasClipboardContent: () => boolean;

	// --- Snap ---
	isSnapEnabled: () => boolean;
	toggleSnap: () => void;
	getSnapGridSize: () => number;

	// --- Virtualization ---
	isVirtualizationEnabled: () => boolean;
	getVisibleNodeKeys: () => string[];
	getVisibleEdgeKeys: () => string[];

	// --- Actions ---
	executeAction: (actionId: string, context: ActionContext) => Promise<ActionExecutionResult>;
	executeEventAction: (event: CanvasEventType, context: ActionContext) => Promise<ActionExecutionResult>;

	// --- Serialization ---
	exportSnapshot: (metadata?: Record<string, unknown>) => CanvasSnapshot;
	importSnapshot: (snapshot: CanvasSnapshot, options?: ImportOptions) => void;
	validateSnapshot: (data: unknown) => ValidationResult;
}

/**
 * Create a headless canvas API from a Jotai store.
 * All canvas operations available without React.
 */
export function createCanvasAPI(store: Store, options: BuildActionHelpersOptions = {}): CanvasAPI {
	const helpers = buildActionHelpers(store, options);

	const api: CanvasAPI = {
		// Selection
		selectNode: (id) => store.set(selectSingleNodeAtom, id),
		addToSelection: (ids) => store.set(addNodesToSelectionAtom, ids),
		clearSelection: () => store.set(clearSelectionAtom),
		getSelectedNodeIds: () => Array.from(store.get(selectedNodeIdsAtom)),
		selectEdge: (edgeId) => store.set(selectEdgeAtom, edgeId),
		clearEdgeSelection: () => store.set(clearEdgeSelectionAtom),
		getSelectedEdgeId: () => store.get(selectedEdgeIdAtom),

		// Viewport
		getZoom: () => store.get(zoomAtom),
		setZoom: (zoom) => store.set(zoomAtom, zoom),
		getPan: () => store.get(panAtom),
		setPan: (pan) => store.set(panAtom, pan),
		resetViewport: () => store.set(resetViewportAtom),
		fitToBounds: (mode, padding) => {
			const fitMode = mode === 'graph' ? FitToBoundsMode.Graph : FitToBoundsMode.Selection;
			store.set(fitToBoundsAtom, { mode: fitMode, padding });
		},
		centerOnNode: (nodeId) => store.set(centerOnNodeAtom, nodeId),

		// Graph
		addNode: (node) => store.set(addNodeToLocalGraphAtom, node),
		removeNode: (nodeId) => store.set(optimisticDeleteNodeAtom, { nodeId }),
		addEdge: (edge) => store.set(addEdgeToLocalGraphAtom, edge),
		removeEdge: (edgeKey) => store.set(optimisticDeleteEdgeAtom, { edgeKey }),
		getNodeKeys: () => store.get(nodeKeysAtom),
		getEdgeKeys: () => store.get(edgeKeysAtom),
		getNodeAttributes: (id) => {
			const graph = store.get(graphAtom);
			return graph.hasNode(id) ? graph.getNodeAttributes(id) : undefined;
		},

		// History
		undo: () => store.set(undoAtom),
		redo: () => store.set(redoAtom),
		canUndo: () => store.get(canUndoAtom),
		canRedo: () => store.get(canRedoAtom),
		recordSnapshot: (label) => store.set(pushHistoryAtom, label),
		clearHistory: () => store.set(clearHistoryAtom),

		// Clipboard
		copy: () => store.set(copyToClipboardAtom),
		cut: () => store.set(cutToClipboardAtom),
		paste: () => store.set(pasteFromClipboardAtom),
		duplicate: () => store.set(duplicateSelectionAtom),
		hasClipboardContent: () => store.get(clipboardAtom) !== null,

		// Snap
		isSnapEnabled: () => store.get(snapEnabledAtom),
		toggleSnap: () => store.set(toggleSnapAtom),
		getSnapGridSize: () => store.get(snapGridSizeAtom),

		// Virtualization
		isVirtualizationEnabled: () => store.get(virtualizationEnabledAtom),
		getVisibleNodeKeys: () => store.get(visibleNodeKeysAtom),
		getVisibleEdgeKeys: () => store.get(visibleEdgeKeysAtom),

		// Actions
		executeAction: (actionId, context) => executeAction(actionId, context, helpers),
		executeEventAction: (event, context) => {
			const mappings = store.get(eventMappingsAtom);
			const actionId = getActionForEvent(mappings, event);
			return executeAction(actionId, context, helpers);
		},

		// Serialization
		exportSnapshot: (metadata) => exportGraph(store, metadata),
		importSnapshot: (snapshot, options) => importGraph(store, snapshot, options),
		validateSnapshot: (data) => validateSnapshot(data),
	};

	return api;
}
