import { describe, it, expect } from 'vitest';
import {
	bezierHorizontal,
	bezierVertical,
	bezierSmart,
	straight,
	stepHorizontal,
	stepVertical,
	stepSmart,
	smoothStep,
	getEdgePathCalculator,
	type EdgePathInput,
} from '../utils/edge-path-calculators';

const SIMPLE_INPUT: EdgePathInput = {
	x1: 0, y1: 0, x2: 200, y2: 100,
	sourceWidth: 200, sourceHeight: 100,
	targetWidth: 200, targetHeight: 100,
};

const VERTICAL_INPUT: EdgePathInput = {
	x1: 100, y1: 0, x2: 120, y2: 400,
	sourceWidth: 200, sourceHeight: 100,
	targetWidth: 200, targetHeight: 100,
};

function expectValidResult(result: { path: string; labelX: number; labelY: number }) {
	expect(result.path).toBeTruthy();
	expect(typeof result.path).toBe('string');
	expect(result.path.startsWith('M ')).toBe(true);
	expect(Number.isFinite(result.labelX)).toBe(true);
	expect(Number.isFinite(result.labelY)).toBe(true);
}

describe('edge-path-calculators', () => {
	describe('bezierHorizontal', () => {
		it('returns valid SVG path with cubic curve', () => {
			const r = bezierHorizontal(SIMPLE_INPUT);
			expectValidResult(r);
			expect(r.path).toContain('C ');
		});

		it('label is at midpoint', () => {
			const r = bezierHorizontal(SIMPLE_INPUT);
			expect(r.labelX).toBe(100); // (0 + 200) / 2
			expect(r.labelY).toBe(50);  // (0 + 100) / 2
		});
	});

	describe('bezierVertical', () => {
		it('returns valid SVG path', () => {
			const r = bezierVertical(VERTICAL_INPUT);
			expectValidResult(r);
			expect(r.path).toContain('C ');
		});
	});

	describe('bezierSmart', () => {
		it('uses horizontal for wide gap', () => {
			const r = bezierSmart(SIMPLE_INPUT);
			const rh = bezierHorizontal(SIMPLE_INPUT);
			expect(r.path).toBe(rh.path);
		});

		it('uses vertical for tall gap', () => {
			const r = bezierSmart(VERTICAL_INPUT);
			const rv = bezierVertical(VERTICAL_INPUT);
			expect(r.path).toBe(rv.path);
		});
	});

	describe('straight', () => {
		it('returns a straight line (M...L)', () => {
			const r = straight(SIMPLE_INPUT);
			expectValidResult(r);
			expect(r.path).toContain('L ');
			expect(r.path).not.toContain('C ');
		});
	});

	describe('stepHorizontal', () => {
		it('returns orthogonal path with horizontal first', () => {
			const r = stepHorizontal(SIMPLE_INPUT);
			expectValidResult(r);
			// Should have 3 line-to commands (M + 3 L's)
			expect((r.path.match(/L /g) || []).length).toBe(3);
		});
	});

	describe('stepVertical', () => {
		it('returns orthogonal path with vertical first', () => {
			const r = stepVertical(SIMPLE_INPUT);
			expectValidResult(r);
			expect((r.path.match(/L /g) || []).length).toBe(3);
		});
	});

	describe('stepSmart', () => {
		it('chooses correct orientation', () => {
			const rWide = stepSmart(SIMPLE_INPUT);
			const rh = stepHorizontal(SIMPLE_INPUT);
			expect(rWide.path).toBe(rh.path);

			const rTall = stepSmart(VERTICAL_INPUT);
			const rv = stepVertical(VERTICAL_INPUT);
			expect(rTall.path).toBe(rv.path);
		});
	});

	describe('smoothStep', () => {
		it('returns valid path with curves', () => {
			const r = smoothStep(SIMPLE_INPUT);
			expectValidResult(r);
		});
	});

	describe('getEdgePathCalculator', () => {
		it('returns correct calculator for each type', () => {
			expect(getEdgePathCalculator('bezier')).toBe(bezierHorizontal);
			expect(getEdgePathCalculator('straight')).toBe(straight);
			expect(getEdgePathCalculator('step')).toBe(stepHorizontal);
			expect(getEdgePathCalculator('smooth-step')).toBe(smoothStep);
		});

		it('returns default for unknown type', () => {
			const calc = getEdgePathCalculator('unknown' as any);
			expect(calc).toBe(bezierHorizontal);
		});
	});
});
