import { describe, it, expect, beforeEach } from 'vitest';
import { createStore } from 'jotai';
import Graph from 'graphology';
import type { GraphNodeAttributes, GraphEdgeAttributes } from '../core/types';
import { graphAtom, graphOptions, graphUpdateVersionAtom } from '../core/graph-store';
import { highestZIndexAtom, nodeKeysAtom, edgeKeysAtom } from '../core/graph-derived';

function makeAttrs(x: number, y: number, zIndex = 0): GraphNodeAttributes {
	return {
		x, y, width: 200, height: 100, size: 15, color: '#888', zIndex,
		dbData: { id: '', graph_id: '', label: '', node_type: '', configuration: null, ui_properties: null, data: null, created_at: '', updated_at: '' },
	};
}

describe('graph-derived', () => {
	let store: ReturnType<typeof createStore>;

	beforeEach(() => {
		store = createStore();
		const graph = new Graph<GraphNodeAttributes, GraphEdgeAttributes>(graphOptions);
		store.set(graphAtom, graph);
	});

	it('highestZIndexAtom returns 0 for empty graph', () => {
		expect(store.get(highestZIndexAtom)).toBe(0);
	});

	it('highestZIndexAtom returns max zIndex', () => {
		const graph = store.get(graphAtom);
		graph.addNode('a', makeAttrs(0, 0, 5));
		graph.addNode('b', makeAttrs(0, 0, 12));
		graph.addNode('c', makeAttrs(0, 0, 3));
		store.set(graphUpdateVersionAtom, (v) => v + 1);
		expect(store.get(highestZIndexAtom)).toBe(12);
	});

	it('nodeKeysAtom reflects graph nodes', () => {
		const graph = store.get(graphAtom);
		graph.addNode('n1', makeAttrs(0, 0));
		graph.addNode('n2', makeAttrs(100, 100));
		store.set(graphUpdateVersionAtom, (v) => v + 1);
		const keys = store.get(nodeKeysAtom);
		expect(keys).toContain('n1');
		expect(keys).toContain('n2');
		expect(keys).toHaveLength(2);
	});

	it('edgeKeysAtom reflects graph edges', () => {
		const graph = store.get(graphAtom);
		graph.addNode('n1', makeAttrs(0, 0));
		graph.addNode('n2', makeAttrs(100, 0));
		graph.addEdgeWithKey('e1', 'n1', 'n2', {
			dbData: { id: 'e1', graph_id: '', source_node_id: 'n1', target_node_id: 'n2', edge_type: '', filter_condition: null, ui_properties: null, data: null, created_at: '', updated_at: '' },
		});
		store.set(graphUpdateVersionAtom, (v) => v + 1);
		const keys = store.get(edgeKeysAtom);
		expect(keys).toContain('e1');
		expect(keys).toHaveLength(1);
	});

	it('nodeKeysAtom is empty for empty graph', () => {
		expect(store.get(nodeKeysAtom)).toHaveLength(0);
	});

	it('edgeKeysAtom is empty for empty graph', () => {
		expect(store.get(edgeKeysAtom)).toHaveLength(0);
	});

	it('nodeKeysAtom updates after adding node', () => {
		const graph = store.get(graphAtom);
		expect(store.get(nodeKeysAtom)).toHaveLength(0);
		graph.addNode('x', makeAttrs(0, 0));
		store.set(graphUpdateVersionAtom, (v) => v + 1);
		expect(store.get(nodeKeysAtom)).toHaveLength(1);
	});

	it('highestZIndexAtom returns single node zIndex', () => {
		const graph = store.get(graphAtom);
		graph.addNode('a', makeAttrs(0, 0, 7));
		store.set(graphUpdateVersionAtom, (v) => v + 1);
		expect(store.get(highestZIndexAtom)).toBe(7);
	});
});
