/**
 * Edge Path Calculators
 *
 * Configurable edge path calculation strategies.
 * Allows customization of how edges are drawn between nodes.
 */

export interface EdgePathInput {
	/** Source point (center of source node) */
	x1: number;
	y1: number;
	/** Target point (center of target node) */
	x2: number;
	y2: number;
	/** Source node dimensions */
	sourceWidth: number;
	sourceHeight: number;
	/** Target node dimensions */
	targetWidth: number;
	targetHeight: number;
}

export interface EdgePathResult {
	/** SVG path d attribute */
	path: string;
	/** Midpoint for label placement */
	labelX: number;
	labelY: number;
}

/**
 * Edge path calculator function type
 */
export type EdgePathCalculator = (input: EdgePathInput) => EdgePathResult;

/**
 * Horizontal Bezier curve (default)
 * Control points extend horizontally from source/target
 */
export const bezierHorizontal: EdgePathCalculator = ({ x1, y1, x2, y2 }) => {
	const dist = Math.abs(x2 - x1);
	const offset = Math.max(dist * 0.5, 50);

	const cp1x = x1 + offset;
	const cp1y = y1;
	const cp2x = x2 - offset;
	const cp2y = y2;

	const path = `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`;

	// Midpoint along bezier curve (approximation)
	const labelX = (x1 + x2) / 2;
	const labelY = (y1 + y2) / 2;

	return { path, labelX, labelY };
};

/**
 * Vertical Bezier curve
 * Control points extend vertically from source/target
 */
export const bezierVertical: EdgePathCalculator = ({ x1, y1, x2, y2 }) => {
	const dist = Math.abs(y2 - y1);
	const offset = Math.max(dist * 0.5, 50);

	const cp1x = x1;
	const cp1y = y1 + offset;
	const cp2x = x2;
	const cp2y = y2 - offset;

	const path = `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`;

	const labelX = (x1 + x2) / 2;
	const labelY = (y1 + y2) / 2;

	return { path, labelX, labelY };
};

/**
 * Smart Bezier curve
 * Chooses horizontal or vertical based on relative positions
 */
export const bezierSmart: EdgePathCalculator = (input) => {
	const { x1, y1, x2, y2 } = input;
	const dx = Math.abs(x2 - x1);
	const dy = Math.abs(y2 - y1);

	// Use horizontal if nodes are more side-by-side, vertical if more stacked
	return dx > dy ? bezierHorizontal(input) : bezierVertical(input);
};

/**
 * Straight line edge
 */
export const straight: EdgePathCalculator = ({ x1, y1, x2, y2 }) => {
	const path = `M ${x1} ${y1} L ${x2} ${y2}`;

	const labelX = (x1 + x2) / 2;
	const labelY = (y1 + y2) / 2;

	return { path, labelX, labelY };
};

/**
 * Step edge (orthogonal) - horizontal first, then vertical
 */
export const stepHorizontal: EdgePathCalculator = ({ x1, y1, x2, y2 }) => {
	const midX = (x1 + x2) / 2;

	const path = `M ${x1} ${y1} L ${midX} ${y1} L ${midX} ${y2} L ${x2} ${y2}`;

	const labelX = midX;
	const labelY = (y1 + y2) / 2;

	return { path, labelX, labelY };
};

/**
 * Step edge (orthogonal) - vertical first, then horizontal
 */
export const stepVertical: EdgePathCalculator = ({ x1, y1, x2, y2 }) => {
	const midY = (y1 + y2) / 2;

	const path = `M ${x1} ${y1} L ${x1} ${midY} L ${x2} ${midY} L ${x2} ${y2}`;

	const labelX = (x1 + x2) / 2;
	const labelY = midY;

	return { path, labelX, labelY };
};

/**
 * Smart step edge
 * Chooses horizontal-first or vertical-first based on relative positions
 */
export const stepSmart: EdgePathCalculator = (input) => {
	const { x1, y1, x2, y2 } = input;
	const dx = Math.abs(x2 - x1);
	const dy = Math.abs(y2 - y1);

	return dx > dy ? stepHorizontal(input) : stepVertical(input);
};

/**
 * Smooth step edge - orthogonal with rounded corners
 */
export const smoothStep: EdgePathCalculator = ({ x1, y1, x2, y2 }) => {
	const midX = (x1 + x2) / 2;
	const radius = Math.min(20, Math.abs(x2 - x1) / 4, Math.abs(y2 - y1) / 2);

	// Handle cases where radius would cause issues
	if (radius < 5 || Math.abs(y2 - y1) < radius * 2) {
		return stepHorizontal({ x1, y1, x2, y2, sourceWidth: 0, sourceHeight: 0, targetWidth: 0, targetHeight: 0 });
	}

	const yDir = y2 > y1 ? 1 : -1;

	const path = `
    M ${x1} ${y1}
    L ${midX - radius} ${y1}
    Q ${midX} ${y1}, ${midX} ${y1 + radius * yDir}
    L ${midX} ${y2 - radius * yDir}
    Q ${midX} ${y2}, ${midX + radius} ${y2}
    L ${x2} ${y2}
  `.replace(/\s+/g, ' ').trim();

	const labelX = midX;
	const labelY = (y1 + y2) / 2;

	return { path, labelX, labelY };
};

/**
 * Available edge path types
 */
export type EdgePathType = 'bezier' | 'bezier-vertical' | 'bezier-smart' | 'straight' | 'step' | 'step-vertical' | 'step-smart' | 'smooth-step';

/**
 * Get calculator by type name
 */
export function getEdgePathCalculator(type: EdgePathType): EdgePathCalculator {
	switch (type) {
		case 'bezier':
			return bezierHorizontal;
		case 'bezier-vertical':
			return bezierVertical;
		case 'bezier-smart':
			return bezierSmart;
		case 'straight':
			return straight;
		case 'step':
			return stepHorizontal;
		case 'step-vertical':
			return stepVertical;
		case 'step-smart':
			return stepSmart;
		case 'smooth-step':
			return smoothStep;
		default:
			return bezierHorizontal;
	}
}

/**
 * Default edge path calculator
 */
export const defaultEdgePathCalculator = bezierHorizontal;
