import Buzz from "../buzz/index.ts"
import { join } from "path"
import { random } from "./index.ts"
import { log } from "console"

export class WaitingSounds {
  typingPlayback?: Buzz.Playback
  speakingPlayback?: Buzz.Playback
  playing = false

  constructor(private player: Buzz.Player) {}

  async start(operatorStream: Buzz.StreamingPlayback) {
    if (this.playing) return // Already playing

    this.playing = true
    this.#startTypingSounds()
    this.#startSpeakingSounds(operatorStream)
  }

  get isPlaying() {
    return this.playing
  }

  async #startTypingSounds() {
    return new Promise<void>(async (resolve) => {
      do {
        const value = Math.random() * 100
        let dir: SoundDir
        if (value > 33) {
          dir = "typing"
        } else {
          dir = "clicking"
        }
        const typingSound = getSound(dir)
        this.typingPlayback = await this.player.play(typingSound)
        await this.typingPlayback.finished()
      } while (this.isPlaying)

      resolve()
    })
  }

  async #startSpeakingSounds(operatorStream: Buzz.StreamingPlayback) {
    const playedSounds = new Set<string>()
    let dir: SoundDir | undefined
    return new Promise<void>(async (resolve) => {
      // Don't start playing speaking sounds until the operator stream has been silent for a bit
      while (operatorStream.bufferEmptyFor < 1500) {
        await Bun.sleep(100)
      }

      do {
        const lastSoundDir = dir
        if (lastSoundDir === "body-noises") {
          dir = "apology"
        } else {
          // sleep for 4-6 seconds
          await Bun.sleep(4000 + Math.random() * 2000)
          const value = Math.random() * 100

          if (value > 95) {
            dir = "body-noises"
          } else {
            dir = "stalling"
          }
        }

        const speakingSound = getSound(dir, Array.from(playedSounds))
        this.speakingPlayback = await this.player.play(speakingSound)
        playedSounds.add(speakingSound)
        await this.speakingPlayback.finished()
      } while (this.isPlaying)
      resolve()
    })
  }

  async stop() {
    log(`🛑 Stopping waiting sounds. Playing? ${this.playing}`)
    if (!this.playing) return
    this.playing = false

    await Promise.all([this.typingPlayback?.stop(), this.speakingPlayback?.finished()])
    log("🛑 Waiting sounds stopped")
  }
}

type SoundDir = (typeof soundDirs)[number]
const soundDirs = [
  "apology",
  "background",
  "body-noises",
  "clicking",
  "greeting",
  "stalling",
  "typing",
] as const
export const soundDir = join(import.meta.dir, "../../", "sounds")

export const getSound = (dir: SoundDir, exclude: string[] = []): string => {
  const glob = new Bun.Glob("*.wav")
  const soundPaths = Array.from(glob.scanSync({ cwd: join(soundDir, dir), absolute: true }))

  const filteredSoundPaths = soundPaths.filter((path) => !exclude.includes(path))
  if (filteredSoundPaths.length === 0) {
    return random(soundPaths)
  }

  return random(filteredSoundPaths)
}
