/**
 * Conversation actions for the staff-chat drawer: load history, send (optimistic),
 * mark read. Reads/writes the shared thread store; sending goes over the socket.
 *
 * Optimistic-send mirrors components/requests/RequestForm.vue: push a temp
 * message with a clientMsgId immediately; the server echoes it back and
 * useChatSocket reconciles by clientMsgId (sets pending=false, fills id).
 */

export const useChat = () => {
  const threads = useChatThreads()
  const unread = useChatUnread()
  const { send } = useChatSocket()

  const user = useCookie<any>('logship_user')
  const me = Number(useCookie('logship_user_id').value || 0)

  const loadHistory = async (peerId: number) => {
    try {
      const res: any = await $fetch('/api/chat/history', {
        query: { peer: peerId },
        headers: useRequestHeaders(['cookie'])
      })
      const records = (res?.records || []).map((m: any) => ({ ...m, pending: false }))
      threads.value = { ...threads.value, [peerId]: records }
    } catch (err) {
      console.error('[useChat] loadHistory failed:', err)
    }
  }

  const loadUnread = async () => {
    try {
      const res: any = await $fetch('/api/chat/unread', { headers: useRequestHeaders(['cookie']) })
      if (res?.counts) unread.value = { ...res.counts }
    } catch (err) {
      console.error('[useChat] loadUnread failed:', err)
    }
  }

  const newClientId = () => {
    try { return crypto.randomUUID() } catch { return `${Date.now()}-${Math.floor(Math.random() * 1e9)}` }
  }

  const sendMessage = (peerId: number, body: string, meta: any = null) => {
    const text = (body || '').trim()
    if (!text || !peerId) return
    const clientMsgId = newClientId()
    const optimistic = {
      id: null,
      senderUserId: me,
      recipientUserId: Number(peerId),
      body: text,
      createdAt: Date.now(),
      readAt: null,
      clientMsgId,
      meta: meta || null,
      pending: true
    }
    const list = threads.value[peerId] ? [...threads.value[peerId]] : []
    list.push(optimistic)
    threads.value = { ...threads.value, [peerId]: list }

    send({
      type: 'message',
      to: Number(peerId),
      body: text,
      clientMsgId,
      fromName: user.value?.Name || user.value?.name || '',
      ...(meta ? { meta } : {})
    })
  }

  const sendTyping = (peerId: number, isTyping: boolean) => {
    if (!peerId) return
    send({ type: 'typing', to: Number(peerId), typing: !!isTyping })
  }

  // Voice message: optimistic bubble (playable instantly via a local object URL),
  // upload over REST. The server fans out the real message over the socket and
  // useChatSocket reconciles by clientMsgId; transcription arrives later as a
  // `message-update`.
  const sendVoice = async (peerId: number, blob: Blob, durationSec = 0, lang = 'auto') => {
    if (!blob || !peerId) return
    const clientMsgId = newClientId()
    const localUrl = typeof URL !== 'undefined' ? URL.createObjectURL(blob) : ''
    const dur = Math.round(durationSec || 0)
    const optimistic = {
      id: null,
      senderUserId: me,
      recipientUserId: Number(peerId),
      body: '🎤 Sprachnachricht',
      createdAt: Date.now(),
      readAt: null,
      clientMsgId,
      meta: { kind: 'voice', localUrl, duration: dur, mime: blob.type || 'audio/webm', transcript: null, transcribing: true },
      pending: true
    }
    const list = threads.value[peerId] ? [...threads.value[peerId]] : []
    list.push(optimistic)
    threads.value = { ...threads.value, [peerId]: list }

    try {
      const fd = new FormData()
      fd.append('to', String(peerId))
      fd.append('clientMsgId', clientMsgId)
      fd.append('duration', String(dur))
      fd.append('fromName', user.value?.Name || user.value?.name || '')
      fd.append('lang', lang)
      fd.append('audio', blob, 'voice.webm')
      await $fetch('/api/chat/voice', {
        method: 'POST',
        headers: useRequestHeaders(['cookie']),
        body: fd
      })
    } catch (err) {
      console.error('[useChat] sendVoice failed:', err)
      const cur = threads.value[peerId] ? [...threads.value[peerId]] : []
      const i = cur.findIndex((m: any) => m.clientMsgId === clientMsgId)
      if (i >= 0) {
        cur[i] = { ...cur[i], pending: false, meta: { ...(cur[i].meta || {}), transcribing: false, error: true } }
        threads.value = { ...threads.value, [peerId]: cur }
      }
    }
  }

  // Image message: take the picked File, downscale it, upload to Strapi (PUBLIC media — same path the
  // mobile pages use; Strapi generates thumbnail/small/medium/large formats), then post the resulting
  // URLs to the chat server (which delivers + pushes). Optimistic bubble previews the local file
  // instantly; useChatSocket reconciles by clientMsgId, swapping in the Strapi thumbnail/full URLs.
  const sendImage = async (peerId: number, file: File) => {
    if (!file || !peerId) return
    const clientMsgId = newClientId()
    const localUrl = typeof URL !== 'undefined' ? URL.createObjectURL(file) : ''
    const optimistic = {
      id: null,
      senderUserId: me,
      recipientUserId: Number(peerId),
      body: '📷 Bild',
      createdAt: Date.now(),
      readAt: null,
      clientMsgId,
      meta: { kind: 'image', localUrl, thumbUrl: localUrl, fullUrl: localUrl },
      pending: true
    }
    const list = threads.value[peerId] ? [...threads.value[peerId]] : []
    list.push(optimistic)
    threads.value = { ...threads.value, [peerId]: list }

    const markError = () => {
      const cur = threads.value[peerId] ? [...threads.value[peerId]] : []
      const i = cur.findIndex((m: any) => m.clientMsgId === clientMsgId)
      if (i >= 0) {
        cur[i] = { ...cur[i], pending: false, meta: { ...(cur[i].meta || {}), error: true } }
        threads.value = { ...threads.value, [peerId]: cur }
      }
    }

    try {
      const config = useRuntimeConfig()
      // 1) downscale + upload to Strapi.
      const { blob } = await downscaleImage(file)
      const up = new FormData()
      up.append('files', blob, 'chat.jpg')
      const res = await fetch(config.public.strapi + '/media-api/upload', {
        method: 'POST',
        headers: { Authorization: 'Bearer ' + (config.public.strapitoken as string) },
        body: up
      })
      if (!res.ok) throw new Error('strapi upload ' + res.status)
      const uploaded = await res.json()
      const f: any = Array.isArray(uploaded) ? uploaded[0] : uploaded
      if (!f) throw new Error('no strapi file')

      const base = config.public.strapi as string
      const fmtUrl = (fmt: any) => (fmt?.hash ? base + '/files-api/' + fmt.hash + fmt.ext : null)
      const orig = f.hash ? base + '/files-api/' + f.hash + f.ext : null
      // thumbnail for the history bubble; large→medium→small for the full-size (click) view.
      const thumbUrl = fmtUrl(f.formats?.thumbnail) || fmtUrl(f.formats?.small) || orig
      const fullUrl = fmtUrl(f.formats?.large) || fmtUrl(f.formats?.medium) || fmtUrl(f.formats?.small) || orig
      if (!thumbUrl) throw new Error('no image url')

      // 2) post the message carrying the Strapi URLs.
      await $fetch('/api/chat/image', {
        method: 'POST',
        headers: useRequestHeaders(['cookie']),
        body: {
          to: peerId,
          clientMsgId,
          fromName: user.value?.Name || user.value?.name || '',
          thumbUrl,
          fullUrl,
          width: f.width || 0,
          height: f.height || 0
        }
      })
    } catch (err) {
      console.error('[useChat] sendImage failed:', err)
      markError()
    }
  }

  // Toggle an emoji reaction on a persisted message (id required). The server
  // relays a `reaction` frame to both parties; useChatSocket updates the chips.
  const react = (messageId: number, emoji: string, on: boolean) => {
    if (!messageId || !emoji) return
    send({ type: 'react', messageId: Number(messageId), emoji, on: !!on })
  }

  const markRead = async (peerId: number) => {
    const u = { ...unread.value }
    delete u[peerId]
    unread.value = u
    try {
      await $fetch('/api/chat/read', {
        method: 'POST',
        headers: useRequestHeaders(['cookie']),
        body: { peer: Number(peerId) }
      })
    } catch (err) {
      console.error('[useChat] markRead failed:', err)
    }
  }

  return { threads, loadHistory, loadUnread, sendMessage, sendVoice, sendImage, react, sendTyping, markRead, me }
}
