export function fetchStreamResponse(
  url: string,
  body: Record<string, any>,
  onMessage: (msg: any) => void,
  signal?: AbortSignal,
): Promise<void> {
  const ChatSDK = (window as any).ChatSDK

  if (ChatSDK && typeof ChatSDK.fetchStreamResponse === 'function') {
    return ChatSDK.fetchStreamResponse(url, body, onMessage, signal)
  }

  const STREAM_TIMEOUT_MS = 30 * 60 * 1000
  body.stream = true
  const decoder = new TextDecoder('utf-8')
  let buffer = ''
  let dataMsgBuffer = ''

  const controller = new AbortController()
  const timeoutId = setTimeout(() => controller.abort(), STREAM_TIMEOUT_MS)
  if (signal) {
    signal.addEventListener('abort', () => controller.abort())
  }
  const effectiveSignal = controller.signal
  const cleanup = () => clearTimeout(timeoutId)

  return new Promise<void>((resolve, reject) => {
    function processMessage(reader: any) {
      reader.read().then((content: any) => {
        if (effectiveSignal.aborted) {
          cleanup()
          resolve()
          return
        }
        buffer += decoder.decode(content.value, { stream: !content.done })
        const lines = buffer.split('\n')
        buffer = lines.pop() as string
        lines.forEach((line) => {
          if (line === '') {
            try {
              onMessage({
                type: 'DATA',
                content: JSON.parse(dataMsgBuffer),
              })
            }
 catch {
            }
            dataMsgBuffer = ''
            return
          }
          const [type] = line.split(':', 1)
          const content = line.substring(type.length + 1)
          if (type === 'data') {
            dataMsgBuffer += content.trim()
          }
 else if (type === '' && content !== '') {
            onMessage({
              type: 'COMMENT',
              content: content.trim(),
            })
          }
 else {
            onMessage({
              type,
              content: content.trim(),
            })
          }
        })
        if (!content.done) {
          processMessage(reader)
        }
 else {
          cleanup()
          resolve()
        }
      }).catch((error: unknown) => {
        if (error instanceof Error && error.name === 'AbortError') {
          cleanup()
          resolve()
          return
        }
        onMessage({ type: 'ERROR', content: error })
        cleanup()
        reject(error)
      })
    }
    const tokenStr = window.localStorage.getItem('__TOKEN__') || '""'
    let token = ''
    try {
      token = JSON.parse(tokenStr)
    }
 catch {
      token = ''
    }

    fetch(url, {
      headers: {
        'accept': 'text/event-stream',
        'Content-Type': 'application/json',
        'X-Token': token,
      },
      method: 'POST',
      body: JSON.stringify(body),
      signal: effectiveSignal,
    })
      .then((response) => {
        return response.body?.getReader()
      })
      .then((reader) => {
        if (!reader) {
          cleanup()
          reject(new Error('Failed to get reader from response'))
          return
        }
        return processMessage(reader)
      })
      .catch((error: unknown) => {
        if (error instanceof Error && error.name === 'AbortError') {
          cleanup()
          resolve()
          return
        }
        onMessage({ type: 'ERROR', content: error })
        cleanup()
        reject(error)
      })
  })
}
