/**
 * Custom hook for computing SVG path data for condition edges.
 * Handles both default cubic bezier paths and manually positioned control
 * point paths with backward flow detection.
 */
import { useMemo } from 'react';
import { Position } from 'reactflow';

interface ControlOffset {
  x: number;
  y: number;
}

interface UseEdgePathProps {
  sourceX: number;
  sourceY: number;
  targetX: number;
  targetY: number;
  sourcePosition: Position;
  targetPosition: Position;
  controlOffset: ControlOffset;
}

type EdgePathResult = [path: string, labelX: number, labelY: number];

/**
 * Calculates base control points for a given source or target position.
 */
function getControlPoint(
  x: number,
  y: number,
  position: Position,
  distance: number,
  _isSource: boolean,
): { x: number; y: number } {
  switch (position) {
    case Position.Right:
      return { x: x + distance, y };
    case Position.Left:
      return { x: x - distance, y };
    case Position.Bottom:
      return { x, y: y + distance };
    case Position.Top:
      return { x, y: y - distance };
    default:
      return { x, y };
  }
}

/**
 * Detects whether the edge flows backward based on source/target positions.
 */
function isBackwardFlow(
  sourcePosition: Position,
  targetPosition: Position,
  sourceX: number,
  sourceY: number,
  targetX: number,
  targetY: number,
): boolean {
  return (
    (sourcePosition === Position.Bottom && targetPosition === Position.Top && targetY < sourceY) ||
    (sourcePosition === Position.Top && targetPosition === Position.Bottom && targetY > sourceY) ||
    (sourcePosition === Position.Right && targetPosition === Position.Left && targetX < sourceX) ||
    (sourcePosition === Position.Left && targetPosition === Position.Right && targetX > sourceX)
  );
}

export function useEdgePath({
  sourceX,
  sourceY,
  targetX,
  targetY,
  sourcePosition,
  targetPosition,
  controlOffset,
}: UseEdgePathProps): EdgePathResult {
  const controlPointX = (sourceX + targetX) / 2 + controlOffset.x;
  const controlPointY = (sourceY + targetY) / 2 + controlOffset.y;

  return useMemo(() => {
    const distance = Math.sqrt(Math.pow(targetX - sourceX, 2) + Math.pow(targetY - sourceY, 2));
    const backward = isBackwardFlow(sourcePosition, targetPosition, sourceX, sourceY, targetX, targetY);

    if (controlOffset.x !== 0 || controlOffset.y !== 0) {
      // Manual control point path
      let baseControlPointDistance = Math.min(distance * 0.25, 50);
      if (backward) {
        baseControlPointDistance = Math.max(baseControlPointDistance, Math.abs(targetY - sourceY) * 0.4, 60);
      }

      const sourceControl = getControlPoint(sourceX, sourceY, sourcePosition, baseControlPointDistance, true);
      const targetControl = getControlPoint(targetX, targetY, targetPosition, baseControlPointDistance, false);

      const midPointX = (sourceX + controlPointX) / 2;
      const midPointY = (sourceY + controlPointY) / 2;

      const path = `M ${sourceX},${sourceY} C ${sourceControl.x},${sourceControl.y} ${midPointX},${midPointY} ${controlPointX},${controlPointY} S ${targetControl.x},${targetControl.y} ${targetX},${targetY}`;

      return [path, controlPointX, controlPointY];
    }

    // Default cubic bezier path
    let controlPointDistance = Math.min(distance * 0.25, 50);
    if (backward) {
      controlPointDistance = Math.max(controlPointDistance, Math.abs(targetY - sourceY) * 0.4, 60);
    }

    const sourceControl = getControlPoint(sourceX, sourceY, sourcePosition, controlPointDistance, true);
    const targetControl = getControlPoint(targetX, targetY, targetPosition, controlPointDistance, false);

    const path = `M ${sourceX},${sourceY} C ${sourceControl.x},${sourceControl.y} ${targetControl.x},${targetControl.y} ${targetX},${targetY}`;

    // Calculate label position at t=0.5 on cubic bezier curve
    const t = 0.5;
    const labelPosX = Math.pow(1 - t, 3) * sourceX + 3 * Math.pow(1 - t, 2) * t * sourceControl.x + 3 * (1 - t) * Math.pow(t, 2) * targetControl.x + Math.pow(t, 3) * targetX;
    const labelPosY = Math.pow(1 - t, 3) * sourceY + 3 * Math.pow(1 - t, 2) * t * sourceControl.y + 3 * (1 - t) * Math.pow(t, 2) * targetControl.y + Math.pow(t, 3) * targetY;

    return [path, labelPosX, labelPosY];
  }, [sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, controlPointX, controlPointY, controlOffset]);
}
