import { Player as PlayerClass } from "./player.js"
import { Recorder as RecorderClass } from "./recorder.js"
import {
  listDevices,
  calculateRMS,
  findDeviceByLabel,
  type AudioFormat as AudioFormatType,
  type Device as DeviceType,
  type Playback as PlaybackType,
  type StreamingPlayback as StreamingPlaybackType,
  type StreamingRecording as StreamingRecordingType,
  type FileRecording as FileRecordingType,
} from "./utils.js"

const player = (label?: string, format?: AudioFormatType) => PlayerClass.create({ label, format })

const recorder = (label?: string, format?: AudioFormatType) =>
  RecorderClass.create({ label, format })

const getVolumeControl = async (cardNumber?: number): Promise<string> => {
  const output = cardNumber
    ? await Bun.$`amixer -c ${cardNumber} scontrols`.text()
    : await Bun.$`amixer scontrols`.text()

  const controls = output
    .split("\n")
    .map((line) => {
      const match = line.match(/Simple mixer control '([^']+)'/)
      return match?.[1]
    })
    .filter(Boolean)

  const playbackControl = controls.find((c) => c !== "Capture")
  if (!playbackControl) {
    throw new Error("No playback mixer control found")
  }

  return playbackControl
}

const setVolume = async (volume: number, label?: string): Promise<void> => {
  const percent = Math.round(volume * 100)

  let cardNumber: number | undefined
  if (label) {
    const device = await findDeviceByLabel(label)
    cardNumber = device.card
  }

  const control = await getVolumeControl(cardNumber)

  const result = cardNumber
    ? await Bun.$`amixer -c ${cardNumber} sset ${control} ${percent}%`.quiet()
    : await Bun.$`amixer sset ${control} ${percent}%`.quiet()

  if (result.exitCode !== 0) {
    throw new Error(`Failed to set volume: ${result.stderr}`)
  }
}

const getVolume = async (label?: string): Promise<number> => {
  let cardNumber: number | undefined
  if (label) {
    const device = await findDeviceByLabel(label)
    cardNumber = device.card
  }

  const control = await getVolumeControl(cardNumber)

  const output = cardNumber
    ? await Bun.$`amixer -c ${cardNumber} sget ${control}`.text()
    : await Bun.$`amixer sget ${control}`.text()

  const match = output.match(/\[(\d+)%\]/)
  if (!match?.[1]) {
    throw new Error("Failed to parse volume from amixer output")
  }

  return parseInt(match[1]) / 100
}

const Buzz = {
  listDevices,
  player,
  recorder,
  setVolume,
  getVolume,
  calculateRMS,
}

declare namespace Buzz {
  export type Playback = PlaybackType
  export type StreamingPlayback = StreamingPlaybackType
  export type StreamingRecording = StreamingRecordingType
  export type FileRecording = FileRecordingType
  export type Player = PlayerClass
  export type Recorder = RecorderClass
}

export default Buzz
