/**
 * POST /api/chat/voice  (multipart/form-data)
 *   fields: to, clientMsgId, duration, fromName, lang   +   file: audio
 *
 * Stores a voice message: the audio blob goes into chat.db, a normal chat
 * message is inserted carrying meta { kind:'voice', audioId, duration, … } and
 * is fanned out over the socket to both parties (so it appears instantly and is
 * playable). Transcription runs in the BACKGROUND (whisper.cpp) and, when ready,
 * patches the message (body = transcript, for search) and emits a
 * `message-update` frame so the bubble fills in its transcript live.
 *
 * Gated to staff. Returns 404 when chat is disabled (kill-switch).
 */

import { gateFromEvent, isStaffUser } from '../../utils/chatAccess'
import { insertMessage, insertVoiceNote, linkVoiceNoteMessage, updateMessageContent } from '../../utils/chatDb'
import { sendToUser, isConnected } from '../../utils/chatPresence'
import { sendPushToUser, buildChatMessagePayload } from '../../utils/pushNotifier'
import { sendFcmToUser } from '../../utils/fcmNotifier'
import { transcribeAudio } from '../../utils/chatTranscribe'

const VOICE_LABEL = '🎤 Sprachnachricht'

export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig()
  if (config.chatEnabled !== true) {
    setResponseStatus(event, 404)
    return { error: 'chat disabled' }
  }

  let userId: number, token: string
  try {
    ({ userId, token } = await gateFromEvent(event))
  } catch (err: any) {
    setResponseStatus(event, err?.statusCode || 403)
    return { error: 'forbidden' }
  }

  const parts = await readMultipartFormData(event)
  if (!parts) { setResponseStatus(event, 400); return { error: 'multipart required' } }

  let to = 0, clientMsgId = '', duration = 0, fromName = '', lang = 'auto'
  let audio: Buffer | null = null
  let mime = 'audio/webm'
  for (const p of parts) {
    if (p.name === 'to') to = Number(p.data.toString())
    else if (p.name === 'clientMsgId') clientMsgId = p.data.toString()
    else if (p.name === 'duration') duration = Number(p.data.toString()) || 0
    else if (p.name === 'fromName') fromName = p.data.toString()
    else if (p.name === 'lang') lang = p.data.toString() || 'de'
    else if (p.name === 'audio') { audio = p.data; if (p.type) mime = p.type }
  }

  if (!to || to === userId) { setResponseStatus(event, 400); return { error: 'bad recipient' } }
  if (!audio || !audio.length) { setResponseStatus(event, 400); return { error: 'audio required' } }
  if (audio.length > 25 * 1024 * 1024) { setResponseStatus(event, 413); return { error: 'audio too large' } }

  // Recipient must be staff (mirror the WS message guard). Only reject on a hard "no".
  if (!isConnected(to)) {
    const staff = await isStaffUser(to, token)
    if (staff === false) { setResponseStatus(event, 403); return { error: 'recipient not allowed' } }
  }

  const voice = insertVoiceNote({
    senderUserId: userId,
    recipientUserId: to,
    mime,
    duration: duration || null,
    bytes: audio
  })
  if (!voice) { setResponseStatus(event, 500); return { error: 'storage failed' } }

  const meta: any = { kind: 'voice', audioId: voice.id, duration: duration || 0, mime, transcript: null, transcribing: true }
  const createdAt = Date.now()
  const saved = insertMessage({
    senderUserId: userId,
    recipientUserId: to,
    body: VOICE_LABEL,
    createdAt,
    clientMsgId: clientMsgId || null,
    meta
  })
  const msg = saved ?? {
    id: null, senderUserId: userId, recipientUserId: to, body: VOICE_LABEL,
    createdAt, readAt: null, clientMsgId: clientMsgId || null, meta
  }
  if (saved?.id) linkVoiceNoteMessage(voice.id, saved.id)

  const out = { type: 'message', message: msg }
  sendToUser(to, out)
  sendToUser(userId, out)

  // Native FCM to ALL the recipient's devices regardless of presence (a backgrounded 2nd device gets
  // nothing from the live socket while another keeps the user "online"). Foreground devices ignore it
  // silently, so no double-notify.
  sendFcmToUser(to, {
    title: fromName || 'Neue Nachricht',
    body: VOICE_LABEL,
    data: { senderUserId: String(userId), url: `/chat?chat=${userId}` },
  }).catch((e: any) => console.error('[ChatVoice] FCM failed:', e?.message))

  // Web push (browsers/PWA) stays offline-only.
  if (!isConnected(to)) {
    const payload = buildChatMessagePayload(fromName || '', VOICE_LABEL, userId)
    sendPushToUser(to, payload).catch((e: any) => console.error('[ChatVoice] offline push failed:', e?.message))
  }

  // Background transcription → patch message + live update both parties.
  if (saved?.id) {
    transcribeAudio(audio, 'voice.webm', lang)
      .then((text) => {
        const finalMeta = { ...meta, transcript: text || '', transcribing: false }
        // body becomes the transcript so spoken words are searchable; keep the
        // label if nothing was recognised.
        const newBody = text ? text : VOICE_LABEL
        updateMessageContent(saved.id, { body: newBody, meta: finalMeta })
        const updated = { ...msg, body: newBody, meta: finalMeta }
        const upd = { type: 'message-update', message: updated }
        sendToUser(to, upd)
        sendToUser(userId, upd)
      })
      .catch((e: any) => console.error('[ChatVoice] transcription error:', e?.message))
  }

  return { status: 200, message: msg }
})
