import { execFileSync } from 'child_process'
import { writeFileSync, unlinkSync, existsSync, mkdirSync } from 'fs'
import { join } from 'path'

/**
 * Server-side voice command recognition using whisper.cpp.
 * Optimized for short utterances: "Done" / "Fertig" / "Repeat" / "Wiederholen".
 *
 * POST /api/mobile/voice-command
 * Body: multipart form — 'audio' file
 *
 * Returns: { command: 'done' | 'repeat' | 'unknown', transcript: string }
 */

const DONE_KEYWORDS = ['done', 'fertig', 'erledigt', 'weiter', 'next', 'nächste', 'ok']
const REPEAT_KEYWORDS = ['repeat', 'wiederholen', 'nochmal', 'noch mal', 'again', 'erneut']

const detectCommand = (text: string): 'done' | 'repeat' | 'unknown' => {
  const lower = text.toLowerCase().trim()
  if (!lower) return 'unknown'

  for (const kw of DONE_KEYWORDS) {
    if (lower.includes(kw)) return 'done'
  }
  for (const kw of REPEAT_KEYWORDS) {
    if (lower.includes(kw)) return 'repeat'
  }
  return 'unknown'
}

export default defineEventHandler(async (event) => {
  const formData = await readMultipartFormData(event)
  if (!formData) {
    return { status: 400, command: 'unknown', transcript: '', message: 'Multipart form data required' }
  }

  let audioBuffer: Buffer | null = null
  let audioExt = 'webm'
  let language = 'en'

  for (const part of formData) {
    if (part.name === 'audio') {
      audioBuffer = part.data
      // Never trust the client filename (it must not reach the filesystem or a
      // process invocation) — only take a whitelisted extension from it.
      const extMatch = /\.(webm|ogg|oga|mp3|m4a|wav|aac|mp4)$/i.exec(part.filename || '')
      audioExt = extMatch ? extMatch[1].toLowerCase() : 'webm'
    } else if (part.name === 'language') {
      language = part.data.toString() || 'en'
    }
  }

  if (!audioBuffer) {
    return { status: 400, command: 'unknown', transcript: '', message: 'Audio file required' }
  }

  // Save audio to temp file
  const tmpDir = join(process.cwd(), '.tmp-voice-cmd')
  if (!existsSync(tmpDir)) mkdirSync(tmpDir, { recursive: true })

  const baseName = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
  const audioPath = join(tmpDir, `${baseName}.${audioExt}`)
  const wavPath = join(tmpDir, `${baseName}.wav`)

  try {
    writeFileSync(audioPath, audioBuffer)

    // Convert to WAV 16kHz mono — execFileSync with an arg array (no shell)
    try {
      execFileSync('ffmpeg', ['-i', audioPath, '-ar', '16000', '-ac', '1', '-y', wavPath], {
        timeout: 15000,
        stdio: ['ignore', 'ignore', 'ignore']
      })
    } catch (e: any) {
      console.error('[VoiceCmd] ffmpeg conversion failed:', e.message)
      return { status: 500, command: 'unknown', transcript: '', message: 'Audio conversion failed' }
    }

    // Transcribe with whisper.cpp
    const persistentDir = '/opt/whisper-cpp'
    const nodeModulesDir = join(process.cwd(), 'node_modules/whisper-node/lib/whisper.cpp')
    const whisperBin = existsSync(join(persistentDir, 'main')) ? join(persistentDir, 'main') : join(nodeModulesDir, 'main')
    const modelFile = existsSync(join(persistentDir, 'ggml-base.bin')) ? join(persistentDir, 'ggml-base.bin') : join(nodeModulesDir, 'models/ggml-base.bin')

    if (!existsSync(whisperBin) || !existsSync(modelFile)) {
      return { status: 500, command: 'unknown', transcript: '', message: 'Whisper not available' }
    }

    // Use short language code for whisper
    const whisperLang = language === 'de-DE' || language === 'de' ? 'de' : 'en'

    let whisperOutput: string
    try {
      whisperOutput = execFileSync(
        whisperBin,
        ['-m', modelFile, '-l', whisperLang, '-f', wavPath],
        { timeout: 30000, encoding: 'utf-8' }
      )
    } catch (e: any) {
      console.error('[VoiceCmd] Whisper failed:', e.stderr || e.message)
      return { status: 500, command: 'unknown', transcript: '', message: 'Transcription failed' }
    }

    // Parse whisper output
    const segments = whisperOutput
      .split('\n')
      .filter(line => line.includes('-->'))
      .map(line => {
        const match = line.match(/\]\s*(.*)$/)
        return match ? match[1].trim() : ''
      })
      .filter(Boolean)

    const transcript = segments.join(' ').trim()
    const command = detectCommand(transcript)

    console.log(`[VoiceCmd] transcript="${transcript}" → command=${command}`)
    return { status: 200, command, transcript }

  } finally {
    try { if (existsSync(audioPath)) unlinkSync(audioPath) } catch {}
    try { if (existsSync(wavPath)) unlinkSync(wavPath) } catch {}
  }
})
