import type { Vec3 } from "../math/Vec3"

/** A 2.5D box: horizontal footprint plus top height. */
export type BoxCollider = {
  shape: "box"
  minX: number
  maxX: number
  minZ: number
  maxZ: number
  top: number
  standable: boolean
}

/** A 2.5D circle: horizontal footprint plus top height. */
export type CircleCollider = {
  shape: "circle"
  x: number
  z: number
  radius: number
  top: number
  standable: boolean
}

/** Finite engine collision capabilities, not game-content identity. */
export type Collider = BoxCollider | CircleCollider

export namespace Collider {
  export function centerX(collider: Collider): number {
    return collider.shape === "circle"
      ? collider.x
      : (collider.minX + collider.maxX) * 0.5
  }

  export function centerZ(collider: Collider): number {
    return collider.shape === "circle"
      ? collider.z
      : (collider.minZ + collider.maxZ) * 0.5
  }

  export function contains(collider: Collider, x: number, z: number): boolean {
    if (collider.shape === "circle") {
      const dx = x - collider.x
      const dz = z - collider.z
      return dx * dx + dz * dz <= collider.radius * collider.radius
    }
    return (
      x >= collider.minX &&
      x <= collider.maxX &&
      z >= collider.minZ &&
      z <= collider.maxZ
    )
  }

  /** Push a horizontal player circle out of one collider. */
  export function pushOut(
    collider: Collider,
    position: Vec3,
    radius: number,
  ): void {
    if (collider.shape === "circle") {
      pushFromCircle(position, radius, collider)
      return
    }
    pushFromBox(position, radius, collider)
  }

  function pushFromBox(position: Vec3, radius: number, box: BoxCollider): void {
    const cx = Math.max(box.minX, Math.min(box.maxX, position.x))
    const cz = Math.max(box.minZ, Math.min(box.maxZ, position.z))
    const dx = position.x - cx
    const dz = position.z - cz
    const distanceSquared = dx * dx + dz * dz
    if (distanceSquared >= radius * radius) {
      return
    }
    if (distanceSquared > 1e-6) {
      const distance = Math.sqrt(distanceSquared)
      const push = (radius - distance) / distance
      position.x += dx * push
      position.z += dz * push
      return
    }
    const left = position.x - box.minX
    const right = box.maxX - position.x
    const near = position.z - box.minZ
    const far = box.maxZ - position.z
    const nearest = Math.min(left, right, near, far)
    if (nearest === left) {
      position.x = box.minX - radius
    } else if (nearest === right) {
      position.x = box.maxX + radius
    } else if (nearest === near) {
      position.z = box.minZ - radius
    } else {
      position.z = box.maxZ + radius
    }
  }

  function pushFromCircle(
    position: Vec3,
    radius: number,
    circle: CircleCollider,
  ): void {
    const dx = position.x - circle.x
    const dz = position.z - circle.z
    const reach = radius + circle.radius
    const distanceSquared = dx * dx + dz * dz
    if (distanceSquared >= reach * reach || distanceSquared < 1e-6) {
      return
    }
    const distance = Math.sqrt(distanceSquared)
    const push = (reach - distance) / distance
    position.x += dx * push
    position.z += dz * push
  }
}
