import { describe, it, expect } from 'vitest';
import { createStore } from 'jotai';
import { createCanvasAPI, type CanvasAPI } from '../core/canvas-api';
import Graph from 'graphology';
import type { GraphNodeAttributes, GraphEdgeAttributes } from '../core/types';
import { graphAtom, graphOptions, currentGraphIdAtom } from '../core/graph-store';
import { nodePositionAtomFamily } from '../core/graph-position';
import { addNodeToLocalGraphAtom } from '../core/graph-mutations';
import { addEdgeToLocalGraphAtom } from '../core/graph-mutations-edges';
import { viewportRectAtom } from '../core/viewport-store';
import type { DBGraphNode, DBGraphEdge } from '../core/types';

function makeDbNode(id: string, x = 0, y = 0): DBGraphNode {
	return {
		id,
		graph_id: 'test-graph',
		label: `Node ${id}`,
		node_type: 'default',
		configuration: null,
		ui_properties: { x, y, width: 200, height: 100, size: 15, zIndex: 0 },
		data: null,
		created_at: '2024-01-01T00:00:00Z',
		updated_at: '2024-01-01T00:00:00Z',
	};
}

function makeDbEdge(id: string, source: string, target: string): DBGraphEdge {
	return {
		id,
		graph_id: 'test-graph',
		source_node_id: source,
		target_node_id: target,
		edge_type: 'default',
		filter_condition: null,
		ui_properties: null,
		data: null,
		created_at: '2024-01-01T00:00:00Z',
		updated_at: '2024-01-01T00:00:00Z',
	};
}

function createTestAPI(): { store: ReturnType<typeof createStore>; api: CanvasAPI } {
	const store = createStore();
	store.set(graphAtom, new Graph<GraphNodeAttributes, GraphEdgeAttributes>(graphOptions));
	store.set(currentGraphIdAtom, 'test-graph');
	const api = createCanvasAPI(store);
	return { store, api };
}

describe('canvas-api (headless)', () => {
	// --- Selection ---

	describe('selection', () => {
		it('selectNode selects a single node', () => {
			const { api } = createTestAPI();
			api.selectNode('n1');
			expect(api.getSelectedNodeIds()).toEqual(['n1']);
		});

		it('addToSelection adds nodes', () => {
			const { api } = createTestAPI();
			api.selectNode('n1');
			api.addToSelection(['n2', 'n3']);
			expect(api.getSelectedNodeIds()).toHaveLength(3);
		});

		it('clearSelection clears all', () => {
			const { api } = createTestAPI();
			api.selectNode('n1');
			api.clearSelection();
			expect(api.getSelectedNodeIds()).toHaveLength(0);
		});

		it('selectEdge / clearEdgeSelection', () => {
			const { api } = createTestAPI();
			api.selectEdge('e1');
			expect(api.getSelectedEdgeId()).toBe('e1');
			api.clearEdgeSelection();
			expect(api.getSelectedEdgeId()).toBeNull();
		});
	});

	// --- Viewport ---

	describe('viewport', () => {
		it('get/set zoom', () => {
			const { api } = createTestAPI();
			api.setZoom(2);
			expect(api.getZoom()).toBe(2);
		});

		it('get/set pan', () => {
			const { api } = createTestAPI();
			api.setPan({ x: 100, y: -50 });
			expect(api.getPan()).toEqual({ x: 100, y: -50 });
		});

		it('resetViewport', () => {
			const { api } = createTestAPI();
			api.setZoom(3);
			api.setPan({ x: 500, y: 500 });
			api.resetViewport();
			expect(api.getZoom()).toBe(1);
			expect(api.getPan()).toEqual({ x: 0, y: 0 });
		});
	});

	// --- Graph ---

	describe('graph', () => {
		it('addNode / getNodeKeys', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1'));
			expect(api.getNodeKeys()).toContain('n1');
		});

		it('addEdge / getEdgeKeys', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1'));
			api.addNode(makeDbNode('n2'));
			api.addEdge(makeDbEdge('e1', 'n1', 'n2'));
			expect(api.getEdgeKeys()).toContain('e1');
		});

		it('removeNode removes node', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1'));
			api.addNode(makeDbNode('n2'));
			api.removeNode('n1');
			expect(api.getNodeKeys()).not.toContain('n1');
			expect(api.getNodeKeys()).toContain('n2');
		});

		it('removeEdge removes edge', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1'));
			api.addNode(makeDbNode('n2'));
			api.addEdge(makeDbEdge('e1', 'n1', 'n2'));
			api.removeEdge('e1');
			expect(api.getEdgeKeys()).not.toContain('e1');
		});

		it('getNodeAttributes returns attributes', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1', 42, 99));
			const attrs = api.getNodeAttributes('n1');
			expect(attrs).toBeDefined();
			expect(attrs!.x).toBe(42);
			expect(attrs!.y).toBe(99);
		});

		it('getNodeAttributes returns undefined for missing node', () => {
			const { api } = createTestAPI();
			expect(api.getNodeAttributes('nonexistent')).toBeUndefined();
		});
	});

	// --- History ---

	describe('history', () => {
		it('canUndo/canRedo start false', () => {
			const { api } = createTestAPI();
			expect(api.canUndo()).toBe(false);
			expect(api.canRedo()).toBe(false);
		});

		it('recordSnapshot enables undo', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1'));
			api.recordSnapshot('test');
			expect(api.canUndo()).toBe(true);
		});

		it('clearHistory clears', () => {
			const { api } = createTestAPI();
			api.recordSnapshot('test');
			api.clearHistory();
			expect(api.canUndo()).toBe(false);
		});
	});

	// --- Clipboard ---

	describe('clipboard', () => {
		it('hasClipboardContent starts false', () => {
			const { api } = createTestAPI();
			expect(api.hasClipboardContent()).toBe(false);
		});

		it('copy then hasClipboardContent', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1'));
			api.selectNode('n1');
			api.copy();
			expect(api.hasClipboardContent()).toBe(true);
		});

		it('paste creates new nodes', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1'));
			api.selectNode('n1');
			api.copy();

			const beforeKeys = api.getNodeKeys().length;
			api.paste();
			expect(api.getNodeKeys().length).toBe(beforeKeys + 1);
		});
	});

	// --- Snap ---

	describe('snap', () => {
		it('isSnapEnabled starts false', () => {
			const { api } = createTestAPI();
			// Default may vary, but we can toggle
			const initial = api.isSnapEnabled();
			api.toggleSnap();
			expect(api.isSnapEnabled()).toBe(!initial);
		});

		it('getSnapGridSize returns a number', () => {
			const { api } = createTestAPI();
			expect(typeof api.getSnapGridSize()).toBe('number');
		});
	});

	// --- Virtualization ---

	describe('virtualization', () => {
		it('isVirtualizationEnabled returns boolean', () => {
			const { api } = createTestAPI();
			expect(typeof api.isVirtualizationEnabled()).toBe('boolean');
		});

		it('getVisibleNodeKeys returns array', () => {
			const { api } = createTestAPI();
			api.addNode(makeDbNode('n1'));
			const keys = api.getVisibleNodeKeys();
			expect(Array.isArray(keys)).toBe(true);
		});
	});

	// --- Actions ---

	describe('actions', () => {
		it('executeAction returns result for unknown action', async () => {
			const { api } = createTestAPI();
			const result = await api.executeAction('nonexistent', {
				eventType: 'NodeClick' as any,
				worldPosition: { x: 0, y: 0 },
				screenPosition: { x: 0, y: 0 },
				modifiers: { shift: false, ctrl: false, alt: false, meta: false },
			});
			// Should handle gracefully
			expect(result).toBeDefined();
		});
	});
});
