import { describe, it, expect, beforeEach } from 'vitest';
import { createStore } from 'jotai';
import Graph from 'graphology';
import type { GraphNodeAttributes, GraphEdgeAttributes, DBGraphNode } from '../core/types';
import { graphAtom, graphOptions, graphUpdateVersionAtom } from '../core/graph-store';
import { addNodeToLocalGraphAtom } from '../core/graph-mutations';
import {
	collapsedGroupsAtom,
	toggleGroupCollapseAtom,
	collapseGroupAtom,
	expandGroupAtom,
	nodeChildrenAtom,
	nodeParentAtom,
	isGroupNodeAtom,
	groupChildCountAtom,
	setNodeParentAtom,
	moveNodesToGroupAtom,
	removeFromGroupAtom,
} from '../core/group-store';

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',
	};
}

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

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

	describe('collapse/expand', () => {
		it('starts with no collapsed groups', () => {
			expect(store.get(collapsedGroupsAtom).size).toBe(0);
		});

		it('collapseGroupAtom adds group to collapsed set', () => {
			store.set(collapseGroupAtom, 'g1');
			expect(store.get(collapsedGroupsAtom).has('g1')).toBe(true);
		});

		it('expandGroupAtom removes group from collapsed set', () => {
			store.set(collapseGroupAtom, 'g1');
			store.set(expandGroupAtom, 'g1');
			expect(store.get(collapsedGroupsAtom).has('g1')).toBe(false);
		});

		it('toggleGroupCollapseAtom toggles state', () => {
			store.set(toggleGroupCollapseAtom, 'g1');
			expect(store.get(collapsedGroupsAtom).has('g1')).toBe(true);
			store.set(toggleGroupCollapseAtom, 'g1');
			expect(store.get(collapsedGroupsAtom).has('g1')).toBe(false);
		});

		it('collapse is idempotent', () => {
			store.set(collapseGroupAtom, 'g1');
			store.set(collapseGroupAtom, 'g1');
			expect(store.get(collapsedGroupsAtom).size).toBe(1);
		});
	});

	describe('parent/child relationships', () => {
		it('setNodeParentAtom sets parentId on node', () => {
			store.set(addNodeToLocalGraphAtom, makeDbNode('parent'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('child'));
			store.set(setNodeParentAtom, { nodeId: 'child', parentId: 'parent' });

			const getParent = store.get(nodeParentAtom);
			expect(getParent('child')).toBe('parent');
		});

		it('nodeChildrenAtom returns children of a group', () => {
			store.set(addNodeToLocalGraphAtom, makeDbNode('parent'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('c1'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('c2'));
			store.set(setNodeParentAtom, { nodeId: 'c1', parentId: 'parent' });
			store.set(setNodeParentAtom, { nodeId: 'c2', parentId: 'parent' });

			const getChildren = store.get(nodeChildrenAtom);
			expect(getChildren('parent')).toHaveLength(2);
		});

		it('isGroupNodeAtom returns true for nodes with children', () => {
			store.set(addNodeToLocalGraphAtom, makeDbNode('parent'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('child'));
			store.set(setNodeParentAtom, { nodeId: 'child', parentId: 'parent' });

			expect(store.get(isGroupNodeAtom)('parent')).toBe(true);
			expect(store.get(isGroupNodeAtom)('child')).toBe(false);
		});

		it('groupChildCountAtom returns correct count', () => {
			store.set(addNodeToLocalGraphAtom, makeDbNode('g'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('c1'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('c2'));
			store.set(setNodeParentAtom, { nodeId: 'c1', parentId: 'g' });
			store.set(setNodeParentAtom, { nodeId: 'c2', parentId: 'g' });

			expect(store.get(groupChildCountAtom)('g')).toBe(2);
		});

		it('prevents self-parenting', () => {
			store.set(addNodeToLocalGraphAtom, makeDbNode('n1'));
			store.set(setNodeParentAtom, { nodeId: 'n1', parentId: 'n1' });
			expect(store.get(nodeParentAtom)('n1')).toBeUndefined();
		});

		it('removeFromGroupAtom clears parentId', () => {
			store.set(addNodeToLocalGraphAtom, makeDbNode('g'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('c'));
			store.set(setNodeParentAtom, { nodeId: 'c', parentId: 'g' });
			expect(store.get(nodeParentAtom)('c')).toBe('g');

			store.set(removeFromGroupAtom, 'c');
			expect(store.get(nodeParentAtom)('c')).toBeUndefined();
		});

		it('moveNodesToGroupAtom moves multiple nodes', () => {
			store.set(addNodeToLocalGraphAtom, makeDbNode('g'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('c1'));
			store.set(addNodeToLocalGraphAtom, makeDbNode('c2'));

			store.set(moveNodesToGroupAtom, { nodeIds: ['c1', 'c2'], groupId: 'g' });

			expect(store.get(nodeChildrenAtom)('g')).toHaveLength(2);
		});
	});
});
