import { BASE_URLS } from '@/config/base-url'
import { useSystemLanguageStore } from '@/store/modules/system-language'
import { useUserStore } from '@/store/modules/user'
import { languageKeyTransform } from '@/utils/language-key-transform'
import { fetchEventSource } from '@microsoft/fetch-event-source'

export interface ResponseData {
  message: string
  reasoningContent: string
  function: { name: string }
}

interface Options {
  onopen?: (response?: Response) => Promise<void>
  onResponse?: (data: ResponseData) => void
  onend?: () => void
  onclose?: () => void
  onerror?: (err: Error) => void
}

export default function fetchEventStreamSource(
  url: string,
  payload: object = {},
  options: Options = {
    onopen: async (_response) => {},
    onResponse: (_data: ResponseData) => {},
    onend: () => {},
    onclose: () => {},
    onerror: (_err: Error) => {},
  },
) {
  const ENV = import.meta.env.VITE_APP_ENV
  const userStore = useUserStore()
  const systemLanguageStore = useSystemLanguageStore()

  const controller = new AbortController()

  fetchEventSource(`${BASE_URLS[ENV || 'DEV']}/api/rest${url}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-request-token': userStore.token,
      'x-lang': languageKeyTransform(systemLanguageStore.currentLanguageInfo.key),
    },
    body: JSON.stringify(payload),
    signal: controller?.signal,

    onopen: async (response) => {
      return options.onopen && options.onopen(response)
    },
    onmessage: (e: { data: string }) => {
      if (e.data === '[DONE]') {
        options.onend && options.onend()
        return
      }

      try {
        const data = JSON.parse(e.data)

        if (data.code === 0 || data.code === '0') {
          data && options.onResponse && options.onResponse(data)
        } else {
          options.onerror && options.onerror(new Error(data.message))

          controller.abort()
          options.onclose && options.onclose()
        }
      } catch (error) {
        options.onerror && options.onerror(error as Error)
      }
    },
    onclose: () => {
      options.onclose && options.onclose()
    },
    onerror: (err) => {
      options.onerror && options.onerror(err)

      throw err
    },
  })

  return controller
}
