// A geometric plane (point + normal) with ray intersection. Ported from worker/src/utils/Plane.ts
// onto the SDK's fluent Vec3 (the mat4 transform() method is omitted — add if needed).

import { Vec3, type Vec3Like } from "../math/vec"
import type { Ray } from "./Ray"

export class Plane {
  /** Unit normal. */
  readonly normal: Vec3
  /** Scalar offset: dot(normal, point). */
  readonly d: number

  constructor(normal: Vec3Like, point: Vec3Like) {
    this.normal = new Vec3(normal).normalize()
    this.d = this.normal.dot(point)
  }

  static fromPoints(a: Vec3Like, b: Vec3Like, c: Vec3Like): Plane {
    const A = new Vec3(a)
    return new Plane(new Vec3(b).sub(A).cross(new Vec3(c).sub(A)), a)
  }

  static fromCoefficients(a: number, b: number, c: number, d: number): Plane {
    const len = Math.hypot(a, b, c)
    const l2 = len * len
    return new Plane([ a, b, c ], [ (a * d) / l2, (b * d) / l2, (c * d) / l2 ])
  }

  /** Ray-plane parameter t (≥0), or null on a miss (parallel / behind origin). */
  intersectRay(ray: Ray): number | null {
    const denom = this.normal.dot(ray.dir)
    if (Math.abs(denom) < 1e-8) return null
    const t = (this.d - this.normal.dot(ray.origin)) / denom
    return t < 0 ? null : t
  }

  /** Like intersectRay but allows hits behind the origin (line-plane). */
  intersectLine(ray: Ray): number | null {
    const denom = this.normal.dot(ray.dir)
    if (Math.abs(denom) < 1e-8) return null
    return (this.d - this.normal.dot(ray.origin)) / denom
  }

  /** Signed distance from a point (positive on the normal's side). */
  signedDistance(point: Vec3Like): number {
    return this.normal.dot(point) - this.d
  }

  /** Project a point onto the plane. */
  projectPoint(point: Vec3Like): Vec3 {
    return new Vec3(point).scaleAndAdd(this.normal, -this.signedDistance(point))
  }
}
