import { describe, it, expect } from 'vitest';
import {
	calculateBounds,
	getNodeCenter,
	setNodeCenter,
	checkNodesOverlap,
	getNodeBounds,
	areNodesClose,
	type Rect,
	type NodeWithDimensions,
} from '../utils/layout';

describe('layout utilities', () => {
	describe('calculateBounds', () => {
		it('returns zero rect for empty array', () => {
			expect(calculateBounds([])).toEqual({ x: 0, y: 0, width: 0, height: 0 });
		});

		it('returns single node bounds', () => {
			const result = calculateBounds([{ x: 10, y: 20, width: 100, height: 50 }]);
			expect(result).toEqual({ x: 10, y: 20, width: 100, height: 50 });
		});

		it('combines multiple nodes', () => {
			const nodes: Rect[] = [
				{ x: 0, y: 0, width: 100, height: 100 },
				{ x: 200, y: 150, width: 100, height: 100 },
			];
			const result = calculateBounds(nodes);
			expect(result).toEqual({ x: 0, y: 0, width: 300, height: 250 });
		});

		it('handles negative coordinates', () => {
			const nodes: Rect[] = [
				{ x: -50, y: -30, width: 100, height: 60 },
				{ x: 100, y: 100, width: 50, height: 50 },
			];
			const result = calculateBounds(nodes);
			expect(result.x).toBe(-50);
			expect(result.y).toBe(-30);
		});
	});

	describe('getNodeCenter', () => {
		it('returns center point', () => {
			const center = getNodeCenter({ x: 100, y: 200, width: 200, height: 100 });
			expect(center).toEqual({ x: 200, y: 250 });
		});
	});

	describe('setNodeCenter', () => {
		it('positions node by center', () => {
			const node: NodeWithDimensions = { x: 0, y: 0, width: 200, height: 100 };
			const result = setNodeCenter(node, 300, 400);
			expect(result.x).toBe(200); // 300 - 100
			expect(result.y).toBe(350); // 400 - 50
		});
	});

	describe('checkNodesOverlap', () => {
		it('detects overlapping nodes', () => {
			const n1 = { x: 0, y: 0, width: 100, height: 100 };
			const n2 = { x: 50, y: 50, width: 100, height: 100 };
			expect(checkNodesOverlap(n1, n2)).toBe(true);
		});

		it('detects non-overlapping nodes', () => {
			const n1 = { x: 0, y: 0, width: 100, height: 100 };
			const n2 = { x: 500, y: 500, width: 100, height: 100 };
			expect(checkNodesOverlap(n1, n2)).toBe(false);
		});
	});

	describe('getNodeBounds', () => {
		it('computes all edges', () => {
			const bounds = getNodeBounds({ x: 10, y: 20, width: 100, height: 50 });
			expect(bounds.left).toBe(10);
			expect(bounds.right).toBe(110);
			expect(bounds.top).toBe(20);
			expect(bounds.bottom).toBe(70);
		});
	});

	describe('areNodesClose', () => {
		it('true for nearby nodes', () => {
			const n1 = { x: 0, y: 0, width: 100, height: 100 };
			const n2 = { x: 50, y: 50, width: 100, height: 100 };
			expect(areNodesClose(n1, n2)).toBe(true);
		});

		it('false for distant nodes', () => {
			const n1 = { x: 0, y: 0, width: 100, height: 100 };
			const n2 = { x: 1000, y: 1000, width: 100, height: 100 };
			expect(areNodesClose(n1, n2)).toBe(false);
		});

		it('respects custom threshold', () => {
			const n1 = { x: 0, y: 0, width: 100, height: 100 };
			const n2 = { x: 100, y: 0, width: 100, height: 100 };
			expect(areNodesClose(n1, n2, 50)).toBe(false);
			expect(areNodesClose(n1, n2, 200)).toBe(true);
		});
	});
});
