/**
 * CHECKPOINTS — falling off the course costs TIME, not the whole run.
 *
 * Each checkpoint is a pair of slim posts flanking the lane. Roll between them
 * and they light gold: from then on a fall respawns you HERE with the clock
 * still running and your paint intact (time is the penalty; paint is progress
 * you already earned). A full restart (R / gamepad Start / after a finish) is
 * the only thing that sends you back to the start line — game.ts owns that and
 * resets us via 'race:respawn'.
 *
 * Linear-course simple: no ordering rule, the most recently crossed pair wins.
 * Emits 'race:checkpoint' {index} on the crossing edge (audio can hook it).
 */
import * as THREE from 'three'
import type { World, Blob } from '../contracts'

export type CheckpointList = Array<[number, number, number]>

export interface CheckpointsHandle {
  /** Respawn point of the last checkpoint crossed this run, or null. */
  last(): THREE.Vector3 | null
}

const POST_H = 2.2
const HALF_GAP = 4 // posts sit at x±HALF_GAP around the point

export function installCheckpoints(
  world: World,
  blob: Blob,
  points: CheckpointList,
): CheckpointsHandle {
  interface Cp {
    at: THREE.Vector3
    mats: THREE.MeshStandardMaterial[]
    lit: boolean
    pulse: number
  }

  const cps: Cp[] = points.map(([x, y, z]) => {
    const mats: THREE.MeshStandardMaterial[] = []
    for (const sx of [-1, 1]) {
      const mat = new THREE.MeshStandardMaterial({
        color: '#cfd8dc', roughness: 0.5, metalness: 0.2,
        emissive: '#cfd8dc', emissiveIntensity: 0.12,
      })
      const post = new THREE.Mesh(new THREE.CylinderGeometry(0.12, 0.16, POST_H, 10), mat)
      post.position.set(x + sx * HALF_GAP, y + POST_H / 2, z)
      post.castShadow = true
      world.scene.add(post)
      mats.push(mat)
    }
    return { at: new THREE.Vector3(x, y, z), mats, lit: false, pulse: 0 }
  })

  let lastLit: Cp | null = null

  world.addSystem({
    update() {
      const t = blob.body.translation()
      for (let i = 0; i < cps.length; i++) {
        const cp = cps[i]
        if (
          Math.abs(t.x - cp.at.x) < HALF_GAP &&
          Math.abs(t.z - cp.at.z) < 1.6 &&
          t.y - cp.at.y > -1 && t.y - cp.at.y < 4
        ) {
          // re-crossing an already-lit pair still moves the respawn point here
          // (matters after a soft respawn sends you back through the course)
          lastLit = cp
          if (!cp.lit) {
            cp.lit = true
            cp.pulse = 1
            world.events.emit('race:checkpoint', { index: i })
          }
        }
      }
    },
  })

  // gold when lit, with a brief pop of glow on the crossing
  world.onFrame((dt) => {
    for (const cp of cps) {
      if (!cp.lit) continue
      cp.pulse = Math.max(0, cp.pulse - dt * 1.5)
      for (const m of cp.mats) {
        m.color.set('#FFD60A')
        m.emissive.set('#FFD60A')
        m.emissiveIntensity = 0.5 + cp.pulse * 1.6
      }
    }
  })

  // full restart → all checkpoints re-arm
  world.events.on('race:respawn', () => {
    lastLit = null
    for (const cp of cps) {
      cp.lit = false
      cp.pulse = 0
      for (const m of cp.mats) {
        m.color.set('#cfd8dc')
        m.emissive.set('#cfd8dc')
        m.emissiveIntensity = 0.12
      }
    }
  })

  return {
    last: () => (lastLit ? lastLit.at : null),
  }
}
