export type Vec3 = { x: number; y: number; z: number }

/** Plain 3D vector math. Every operation returns a fresh object (no in-place
 *  mutation) to keep call sites easy to reason about. */
export namespace Vec3 {
  export function add(a: Vec3, b: Vec3): Vec3 {
    return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }
  }

  export function sub(a: Vec3, b: Vec3): Vec3 {
    return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }
  }

  export function scale(v: Vec3, s: number): Vec3 {
    return { x: v.x * s, y: v.y * s, z: v.z * s }
  }

  export function dot(a: Vec3, b: Vec3): number {
    return a.x * b.x + a.y * b.y + a.z * b.z
  }

  export function cross(a: Vec3, b: Vec3): Vec3 {
    return {
      x: a.y * b.z - a.z * b.y,
      y: a.z * b.x - a.x * b.z,
      z: a.x * b.y - a.y * b.x,
    }
  }

  export function length(v: Vec3): number {
    return Math.sqrt(dot(v, v))
  }

  export function normalize(v: Vec3): Vec3 {
    const len = length(v)
    // Fresh zero (not the input alias) to keep the namespace-wide no-aliasing
    // guarantee even on the degenerate case.
    return len === 0 ? { x: 0, y: 0, z: 0 } : scale(v, 1 / len)
  }
}
