/**
 * Colour zones (Lane G) — laid ON the existing greybox (course spans z 30 → -70,
 * see greybox.ts). Two jobs:
 *
 *  1. PAINT BY ROUTE. Each stretch of track gets a single-colour puddle run so a
 *     racer picks up ONE dominant colour there instead of confetti. That matters
 *     because the buff threshold is 20% of a single colour (coverage-math.ts) —
 *     three colours diluting each other never crossed it (observed live). One
 *     colour per zone makes buffs actually activate.
 *
 *  2. THE MEGA/MINI TEACHING FORK (machine district, z -37..-58). Paint = size =
 *     route:
 *       LEFT lane  (x<0): PURPLE puddle → MEGA (grow + heavy) → trips a pressure
 *                         plate → SpringBoot yeet. The course teaches paint=weight.
 *       RIGHT lane (x>3): PINK puddle → MINI (shrink) → fits a deliberately LOW
 *                         tunnel (pink roof slab at y≈1.0) that shortcuts toward
 *                         the finish. A normal/MEGA blob bonks the roof.
 *
 * Zone → colour → buff intent (GDD §6):
 *   RED    ramp approach   BURN speed helps the climb
 *   GREEN  shelves + slope GRIP helps the slope / future gravity flips
 *   BLUE   milk river      SLICK waterproof — your paint survives the water
 *   PURPLE left fork       MEGA heavy → pressure-plate route
 *   PINK   right fork      MINI small → tunnel shortcut
 *
 * Cannon placement decision: installZones does NOT instantiate PaintCannons. It
 * RETURNS `cannonConfigs` (plain data) so integration builds them through the one
 * cannon-staging path it already owns in game.ts (target=blob.mesh, muzzle tuning,
 * etc.), avoiding double-placement. Puddles are the guaranteed paint; cannons are
 * bonus pressure, so ignoring the configs still satisfies the ≥40% route rule.
 */
import * as THREE from 'three'
import type { Blob, PaintColor, World } from '../contracts'
import { PALETTE } from '../contracts'
import type { PaintSkin } from '../paint/skin'
import { installPuddles, type PaintPuddle, type PaintPuddleConfig } from '../paint/puddles'
import { createPressurePlate, createSpringBoot, type Vec3 } from '../machine'

/** A cannon integration should stage for a zone (fires that zone's colour). */
export interface ZoneCannonConfig {
  color: PaintColor
  position: Vec3
  /** Fires on `machine:signal {id}` matching this (Lane C wiring). */
  triggerId: string
  /** Suggested seconds between auto-fires. */
  interval: number
}

export interface TunnelInfo {
  /** World Y of the roof's underside — only blobs shorter than this fit. */
  roofUndersideY: number
  center: Vec3
  /** Roof slab half-extents [x, y, z]. */
  half: Vec3
}

export interface ZonesHandle {
  puddles: PaintPuddle[]
  cannonConfigs: ZoneCannonConfig[]
  /** Left-fork pressure plate: trips at baseMass*1.25 (needs a MEGA blob). */
  plateId: string
  plateThreshold: number
  bootSignal: string
  tunnel: TunnelInfo
  totalStamps(): number
}

// ---------------------------------------------------------------------------
// Puddle layout — absolute course coordinates (see greybox.ts surface heights).
//   base floor top y≈0 · shelves top y≈3.5 · river top y≈0.12 · machine top y≈0.24
// Centre-line strips (x≈0) coat a hold-forward run; fork strips sit off-centre.
// ---------------------------------------------------------------------------
const PUDDLE_LAYOUT: PaintPuddleConfig[] = [
  // RED zone (ramp approach, z≈22..8) — wide centre strip off the plateau + onto
  // the ramp base. Generous so BURN reliably activates before the climb.
  { color: 'red', position: [0, 0.05, 18.5], size: [9, 9], splatRadius: 0.34 },
  { color: 'red', position: [0, 1.1, 12], size: [8, 6], splatRadius: 0.34 },

  // GREEN zone (shelves + slope, z≈8..-24) — both blue shelves are flat & always
  // rolled, so GRIP activates there; a strip on the slope tops it up.
  { color: 'green', position: [0, 3.55, 6], size: [11, 7], splatRadius: 0.34 },
  { color: 'green', position: [0, 3.55, -6], size: [11, 7], splatRadius: 0.34 },
  { color: 'green', position: [0, 1.5, -15.5], size: [9, 6], splatRadius: 0.34 },

  // BLUE zone (milk river, z≈-24..-37) — long centre strip across the crossing so
  // SLICK/waterproof is up before you hit the water.
  { color: 'blue', position: [0, 0.15, -30], size: [11, 13], splatRadius: 0.36 },

  // PURPLE/PINK fork (machine district, z≈-37..-58) on the machine-area floor.
  // Strips sit at the district ENTRANCE so the blob is fully MEGA/MINI BEFORE it
  // reaches the plate / tunnel further in.
  // Widened toward the racing line (integration tune): at race speed a fork
  // strip at x±6 was unreachable by the time you could steer post-launcher.
  // A ~1.5u neutral gap at x≈0 still lets a centre runner stay unforked.
  { color: 'purple', position: [-4.5, 0.27, -40], size: [8, 8], splatRadius: 0.34 },
  { color: 'pink', position: [5, 0.27, -40], size: [8, 8], splatRadius: 0.34 },
  // Neutral-centre red top-up (integration tune): the fast launcher route is
  // airborne through most strips; this grounded stretch lets a centre runner's
  // red cross the 20% threshold — every route should earn SOME buff.
  { color: 'red', position: [0, 0.27, -44], size: [2.2, 12], splatRadius: 0.34 },
]

// Fork geometry constants (obstacles sit PAST the entrance strips).
const PLATE_POS: Vec3 = [-6, 0.24, -47]
const BOOT_POS: Vec3 = [-6, 0.2, -49.5]
const PLATE_ID = 'zone-plate-purple'
const BOOT_SIGNAL = 'zone-boot-purple'

// Roof underside = 1.27 - 0.15 = 1.12. On the machine floor (top y≈0.24) a blob
// rests with its belly down, so its top ≈ 0.24 + 2·(r·size):
//   normal (size 1.0)  → top ≈ 1.24  → BONK
//   ≥35% pink (≤0.846) → top ≤ 1.086 → CLEARS
//   full MINI (0.62)   → top ≈ 0.86  → easy clearance
const TUNNEL_CENTER: Vec3 = [6.5, 1.27, -51]
const TUNNEL_HALF: Vec3 = [3.5, 0.15, 7] // spans z -58..-44

/**
 * Build the colour zones + the MEGA/MINI fork onto the live greybox and wire the
 * puddle paint system. Integration calls this once, after the blob/skin exist.
 */
export function installZones(world: World, blob: Blob, skin: PaintSkin): ZonesHandle {
  const { scene, physics, rapier } = world

  // ---- puddles (guaranteed paint by route) ----
  const puddlesHandle = installPuddles(world, blob, skin, PUDDLE_LAYOUT)

  // ---- LEFT fork: PURPLE → MEGA → pressure plate → boot yeet ----
  // Clean blob mass is ~1; a MEGA (purple) blob is heavy enough to cross *1.25.
  const baseMass = blob.body.mass()
  const plateThreshold = baseMass * 1.25
  createPressurePlate(world, {
    id: PLATE_ID,
    position: PLATE_POS,
    massThreshold: plateThreshold,
    emits: BOOT_SIGNAL,
    size: [4, 4],
  })
  createSpringBoot(world, {
    id: 'zone-boot',
    position: BOOT_POS,
    impulse: baseMass * 20, // sized for a heavy MEGA blob to carry toward the finish
    direction: [0, 1, -0.7], // up + forward (-Z)
    onSignal: BOOT_SIGNAL,
    strikeSize: [2.6, 1.4, 3],
  })

  // ---- RIGHT fork: PINK → MINI → LOW TUNNEL shortcut ----
  buildTunnel(scene, physics, rapier)

  // ---- cannons (returned as data for integration to stage) ----
  const cannonConfigs: ZoneCannonConfig[] = [
    // RED crossfire: the existing red position + a mirror across the lane.
    { color: 'red', position: [-9, 2.5, 14], triggerId: 'cannon-red', interval: 2.2 },
    { color: 'red', position: [9, 2.5, 14], triggerId: 'cannon-red-2', interval: 2.2 },
    // GREEN over the shelves.
    { color: 'green', position: [9, 4.8, -6], triggerId: 'cannon-green', interval: 2.2 },
    // BLUE over the river.
    { color: 'blue', position: [-9, 2.0, -30], triggerId: 'cannon-blue', interval: 2.4 },
    // Fork colours fire on their own lanes.
    { color: 'purple', position: [-11, 2.0, -43], triggerId: 'cannon-purple', interval: 2.6 },
    { color: 'pink', position: [12, 2.0, -43], triggerId: 'cannon-pink', interval: 2.6 },
  ]

  return {
    puddles: puddlesHandle.puddles,
    cannonConfigs,
    plateId: PLATE_ID,
    plateThreshold,
    bootSignal: BOOT_SIGNAL,
    tunnel: { roofUndersideY: TUNNEL_CENTER[1] - TUNNEL_HALF[1], center: TUNNEL_CENTER, half: TUNNEL_HALF },
    totalStamps: puddlesHandle.totalStamps,
  }
}

/**
 * The pink low tunnel: a visibly-low pink roof slab (collider) over the right
 * lane, on stubby posts. Only a MINI blob clears its underside; taller blobs bonk
 * and must take another lane. Posts are cosmetic (no lane-blocking colliders).
 */
function buildTunnel(
  scene: THREE.Scene,
  physics: World['physics'],
  rapier: World['rapier'],
): void {
  const [cx, cy, cz] = TUNNEL_CENTER
  const [hx, hy, hz] = TUNNEL_HALF

  const roofMat = new THREE.MeshStandardMaterial({
    color: PALETTE.pink,
    roughness: 0.4,
    metalness: 0.1,
    transparent: true,
    opacity: 0.82,
    emissive: PALETTE.pink,
    emissiveIntensity: 0.18,
  })
  const roof = new THREE.Mesh(new THREE.BoxGeometry(hx * 2, hy * 2, hz * 2), roofMat)
  roof.position.set(cx, cy, cz)
  roof.castShadow = true
  roof.receiveShadow = true
  scene.add(roof)
  // Solid roof collider — this is what bonks a too-tall blob.
  physics.createCollider(
    rapier.ColliderDesc.cuboid(hx, hy, hz).setTranslation(cx, cy, cz),
  )

  // "MIND YOUR HEAD" clearance bar hanging just under the lip so the low ceiling
  // reads before you reach it (visual only).
  const barMat = new THREE.MeshStandardMaterial({
    color: PALETTE.pink, emissive: PALETTE.pink, emissiveIntensity: 0.4, roughness: 0.5,
  })
  const bar = new THREE.Mesh(new THREE.BoxGeometry(hx * 2, 0.06, 0.12), barMat)
  bar.position.set(cx, cy - hy - 0.02, cz + hz) // entrance lip
  scene.add(bar)

  // cosmetic corner posts (no colliders — they don't block the lane)
  const postMat = new THREE.MeshStandardMaterial({ color: '#c94f86', roughness: 0.6 })
  for (const sx of [-1, 1]) {
    for (const sz of [-1, 1]) {
      const post = new THREE.Mesh(new THREE.CylinderGeometry(0.16, 0.16, cy, 10), postMat)
      post.position.set(cx + sx * hx, cy * 0.5, cz + sz * hz)
      post.castShadow = true
      scene.add(post)
    }
  }
}
