/**
 * Lane F demo — audio soundboard.
 *
 * Verifies every synthesized one-shot in isolation. Buttons emit the SAME frozen
 * wire events the game emits, through the real `installAudio()` wiring (so the
 * event → sound mapping and the mute button are exercised too, not just the raw
 * synth functions). An impact slider drives the squish scaling, a colour picker
 * drives the per-colour splat pitch, and a "new best" toggle drives the fanfare
 * flourish.
 *
 * Autoplay: nothing makes sound until the first click (which unlocks the
 * AudioContext) — matching the browser policy. There is no title screen here, so
 * the first-gesture fallback in installAudio does the unlocking.
 *
 * Buttons / keys (also handy for integration):
 *   squish landing   → blob:landed {impact}     key 1
 *   boing jump       → blob:jumped              key 2
 *   splat (colour)   → paint:splatted {blob}    key 3
 *   sparkle cleanse  → paint:request-cleanse    key 4
 *   clank + windup   → machine:signal           key 5
 *   race start tick  → race:started             key 6
 *   fanfare          → race:finished            key 7
 *   SPLAT STORM (10) → 10× paint:splatted       key 8   (voice-cap stress test)
 *   ambient hum      → toggle conveyor room tone key 9
 */
import type { PaintColor, World } from '../contracts'
import { PALETTE } from '../contracts'
import { installAudio } from '../audio/install'
import { startAmbientHum, type Ambient } from '../audio/sounds'

// --- minimal event bus (same shape as world.ts's, so installAudio is happy) ---
type Handler = (p: unknown) => void
function makeBus(): World['events'] {
  const map = new Map<string, Set<Handler>>()
  return {
    on(name: string, fn: Handler) {
      if (!map.has(name)) map.set(name, new Set())
      map.get(name)!.add(fn)
      return () => map.get(name)!.delete(fn)
    },
    emit(name: string, payload?: unknown) {
      map.get(name)?.forEach((fn) => fn(payload))
    },
  }
}

const events = makeBus()
// installAudio only ever touches world.events — a partial cast is safe here.
const { engine } = installAudio({ events } as unknown as World)

// ---------------------------------------------------------------- unlock badge
const unlockBadge = document.getElementById('unlock')!
const refreshUnlock = (): void => {
  if (engine.ready) {
    unlockBadge.textContent = '🔓 AudioContext live — hit the pads.'
    unlockBadge.classList.add('on')
  }
}
addEventListener('pointerdown', () => setTimeout(refreshUnlock, 0), { once: false })

// ------------------------------------------------------------------ controls UI
const colors = Object.keys(PALETTE) as PaintColor[]
let impact = 6
let color: PaintColor = 'red'
let newBest = false

const pads = document.getElementById('pads')!

interface PadDef {
  label: string
  sub: string
  key: string
  fire: () => void
}

let ambient: Ambient | null = null

const defs: PadDef[] = [
  { label: '💥 squish', sub: 'blob:landed', key: '1', fire: () => events.emit('blob:landed', { impact }) },
  { label: '🟢 boing', sub: 'blob:jumped', key: '2', fire: () => events.emit('blob:jumped', {}) },
  { label: '🎨 splat', sub: 'paint:splatted', key: '3', fire: () => events.emit('paint:splatted', { color, target: 'blob' }) },
  { label: '✨ sparkle', sub: 'paint:request-cleanse', key: '4', fire: () => events.emit('paint:request-cleanse', { fraction: 1 }) },
  { label: '⚙️ clank', sub: 'machine:signal', key: '5', fire: () => events.emit('machine:signal', { id: 'demo' }) },
  { label: '🏁 tick', sub: 'race:started', key: '6', fire: () => events.emit('race:started', {}) },
  { label: '🎉 fanfare', sub: 'race:finished', key: '7', fire: () => events.emit('race:finished', newBest ? { time: 9, best: 9 } : { time: 12, best: 9 }) },
  {
    label: '🌪 splat storm', sub: '10× in 1s (voice cap)', key: '8',
    fire: () => {
      for (let i = 0; i < 10; i++) {
        const c = colors[i % colors.length]
        setTimeout(() => events.emit('paint:splatted', { color: c, target: 'blob' }), i * 100)
      }
    },
  },
  {
    label: '🛗 ambient', sub: 'toggle room tone', key: '9',
    fire: () => {
      if (ambient) { ambient.stop(); ambient = null }
      else ambient = startAmbientHum(engine)
    },
  },
]

for (const d of defs) {
  const b = document.createElement('button')
  b.className = 'pad'
  b.innerHTML = `${d.label}<small>${d.sub} · key ${d.key}</small>`
  b.addEventListener('click', () => { d.fire(); refreshUnlock() })
  pads.appendChild(b)
}

addEventListener('keydown', (e) => {
  const d = defs.find((x) => x.key === e.key)
  if (d) { d.fire(); refreshUnlock() }
})

// ---------------------------------------------------------------- sliders panel
const panel = document.createElement('div')
panel.className = 'ctl'
panel.innerHTML = `
  <h2>parameters</h2>
  <div class="row">
    <label for="impact">impact (squish)</label>
    <input type="range" id="impact" min="0.8" max="18" step="0.1" value="${impact}">
    <span class="val" id="impactVal">${impact.toFixed(1)}</span>
  </div>
  <div class="row">
    <label for="color">splat colour</label>
    <select id="color">${colors.map((c) => `<option value="${c}">${c}</option>`).join('')}</select>
    <span class="val" id="colorSwatch">■</span>
  </div>
  <div class="row">
    <label for="best">fanfare = new best</label>
    <input type="checkbox" id="best">
  </div>
`
document.getElementById('app')!.appendChild(panel)

const impactEl = document.getElementById('impact') as HTMLInputElement
const impactVal = document.getElementById('impactVal')!
impactEl.addEventListener('input', () => {
  impact = Number(impactEl.value)
  impactVal.textContent = impact.toFixed(1)
})

const colorEl = document.getElementById('color') as HTMLSelectElement
const swatch = document.getElementById('colorSwatch') as HTMLElement
const paintSwatch = (): void => { swatch.style.color = PALETTE[color] }
colorEl.addEventListener('change', () => { color = colorEl.value as PaintColor; paintSwatch() })
paintSwatch()

const bestEl = document.getElementById('best') as HTMLInputElement
bestEl.addEventListener('change', () => { newBest = bestEl.checked })

console.log('[lane-f] soundboard ready — click a pad (or keys 1-9) to fire wire events.')
