/** Pure distance and geometric helper functions — no state dependencies */

import type { AABB } from '../types.js';

export function getAABB(x: number, y: number, w: number, h: number, rotation: number): AABB {
  if (!rotation) return { x, y, w, h };
  const cx = x + w / 2;
  const cy = y + h / 2;
  const rad = (rotation * Math.PI) / 180;
  const cos = Math.abs(Math.cos(rad));
  const sin = Math.abs(Math.sin(rad));
  const bw = w * cos + h * sin;
  const bh = w * sin + h * cos;
  return { x: cx - bw / 2, y: cy - bh / 2, w: bw, h: bh };
}

/** Distance from a point to a line segment */
export function distPointToSegment(px: number, py: number, x1: number, y1: number, x2: number, y2: number): number {
  const dx = x2 - x1, dy = y2 - y1;
  const len2 = dx * dx + dy * dy;
  if (len2 === 0) return Math.sqrt((px - x1) ** 2 + (py - y1) ** 2);
  const t = Math.max(0, Math.min(1, ((px - x1) * dx + (py - y1) * dy) / len2));
  const nearX = x1 + t * dx, nearY = y1 + t * dy;
  return Math.sqrt((px - nearX) ** 2 + (py - nearY) ** 2);
}

/**
 * Distance from a point to an annular sector (arc band).
 * Point is in local space: arc center at origin, Y points UP,
 * sector sweeps CCW from angle 0 to sweepRad.
 */
export function distToAnnularSector(px: number, py: number, innerR: number, outerR: number, sweepRad: number): number {
  const r = Math.sqrt(px * px + py * py);
  let theta = Math.atan2(py, px);
  if (theta < -0.001) theta += 2 * Math.PI;

  const inAngle = theta >= -0.001 && theta <= sweepRad + 0.001;

  if (inAngle) {
    if (r >= innerR && r <= outerR) return 0;
    if (r < innerR) return innerR - r;
    return r - outerR;
  }

  let minDist = Infinity;
  minDist = Math.min(minDist, distPointToSegment(px, py, innerR, 0, outerR, 0));

  const ec = Math.cos(sweepRad), es = Math.sin(sweepRad);
  minDist = Math.min(minDist, distPointToSegment(px, py, innerR * ec, innerR * es, outerR * ec, outerR * es));

  const clampedTheta = Math.max(0, Math.min(sweepRad, theta < 0 ? theta + 2 * Math.PI : theta));
  if (clampedTheta >= 0 && clampedTheta <= sweepRad) {
    const oc = Math.cos(clampedTheta), os = Math.sin(clampedTheta);
    minDist = Math.min(minDist, Math.sqrt((px - outerR * oc) ** 2 + (py - outerR * os) ** 2));
    if (innerR > 0) {
      minDist = Math.min(minDist, Math.sqrt((px - innerR * oc) ** 2 + (py - innerR * os) ** 2));
    }
  }

  return minDist;
}

/** Distance from a point in world space to an OBB */
export function pointToOBBDist(px: number, py: number, bx: number, by: number, bw: number, bh: number, brot: number): number {
  const bcx = bx + bw / 2, bcy = by + bh / 2;
  const rad = -(brot || 0) * Math.PI / 180;
  const cos = Math.cos(rad), sin = Math.sin(rad);
  const dx = px - bcx, dy = py - bcy;
  const lx = dx * cos - dy * sin;
  const ly = dx * sin + dy * cos;
  const hw = bw / 2, hh = bh / 2;
  const cx = Math.max(-hw, Math.min(hw, lx));
  const cy = Math.max(-hh, Math.min(hh, ly));
  return Math.sqrt((lx - cx) ** 2 + (ly - cy) ** 2);
}

/** Test if a point is inside a convex polygon */
export function pointInConvexPolygon(px: number, py: number, verts: [number, number][]): boolean {
  let sign = 0;
  for (let i = 0; i < verts.length; i++) {
    const [x1, y1] = verts[i];
    const [x2, y2] = verts[(i + 1) % verts.length];
    const cross = (x2 - x1) * (py - y1) - (y2 - y1) * (px - x1);
    if (cross !== 0) {
      if (sign === 0) sign = cross > 0 ? 1 : -1;
      else if ((cross > 0 ? 1 : -1) !== sign) return false;
    }
  }
  return true;
}

/** Distance from a point to a convex polygon (0 if inside) */
export function pointToConvexPolygonDist(px: number, py: number, verts: [number, number][]): number {
  if (pointInConvexPolygon(px, py, verts)) return 0;
  let minDist = Infinity;
  for (let i = 0; i < verts.length; i++) {
    const [x1, y1] = verts[i];
    const [x2, y2] = verts[(i + 1) % verts.length];
    minDist = Math.min(minDist, distPointToSegment(px, py, x1, y1, x2, y2));
  }
  return minDist;
}

/** Edge distance between two axis-aligned bounding boxes */
export function edgeDistance(
  ax: number, ay: number, aw: number, ah: number,
  bx: number, by: number, bw: number, bh: number
): number {
  const gapX = Math.max(0, Math.max(ax, bx) - Math.min(ax + aw, bx + bw));
  const gapY = Math.max(0, Math.max(ay, by) - Math.min(ay + ah, by + bh));
  return Math.sqrt(gapX * gapX + gapY * gapY);
}

export function clamp(value: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, value));
}
