export type LineOrientation = 'horizontal' | 'vertical';

export type DrawLineLike = {
  type: 'line';
  x1: number;
  y1: number;
  x2: number;
  y2: number;
  orientation: LineOrientation;
};

export type DrawShapeLike =
  | DrawLineLike
  | {
      type: 'rect';
      x: number;
      y: number;
      width: number;
      height: number;
    };

function isBetween(value: number, start: number, end: number) {
  const min = Math.min(start, end);
  const max = Math.max(start, end);
  return value >= min && value <= max;
}

function resolveNearestCoordinate(
  start: number,
  candidates: number[],
  direction: -1 | 1,
) {
  return candidates.reduce<number | null>((closest, candidate) => {
    if ((candidate - start) * direction <= 0) {
      return closest;
    }

    if (closest === null) {
      return candidate;
    }

    return Math.abs(candidate - start) < Math.abs(closest - start) ? candidate : closest;
  }, null);
}

function resolveLineOrientation(
  startX: number,
  startY: number,
  pointerX: number,
  pointerY: number,
): LineOrientation {
  const deltaX = pointerX - startX;
  const deltaY = pointerY - startY;
  return Math.abs(deltaX) >= Math.abs(deltaY) ? 'vertical' : 'horizontal';
}

export function resolveLineDraft(
  startX: number,
  startY: number,
  pointerX: number,
  pointerY: number,
  shapes: DrawShapeLike[],
  canvasWidth: number,
  canvasHeight: number,
  preferredOrientation?: LineOrientation,
) {
  const orientation =
    preferredOrientation ?? resolveLineOrientation(startX, startY, pointerX, pointerY);

  if (orientation === 'horizontal') {
    const crossingXs = shapes.flatMap<number>((shape) => {
      if (shape.type !== 'line' || shape.orientation !== 'vertical') {
        return [];
      }

      return isBetween(startY, shape.y1, shape.y2) ? [shape.x1] : [];
    });
    const previousStop = resolveNearestCoordinate(startX, crossingXs, -1);
    const nextStop = resolveNearestCoordinate(startX, crossingXs, 1);

    return {
      startX: previousStop ?? 0,
      startY,
      endX: nextStop ?? canvasWidth,
      endY: startY,
      orientation,
    };
  }

  const crossingYs = shapes.flatMap<number>((shape) => {
    if (shape.type !== 'line' || shape.orientation !== 'horizontal') {
      return [];
    }

    return isBetween(startX, shape.x1, shape.x2) ? [shape.y1] : [];
  });
  const previousStop = resolveNearestCoordinate(startY, crossingYs, -1);
  const nextStop = resolveNearestCoordinate(startY, crossingYs, 1);

  return {
    startX,
    startY: previousStop ?? 0,
    endX: startX,
    endY: nextStop ?? canvasHeight,
    orientation,
  };
}
