/**
 * SSE（Server-Sent Events）组合式函数
 * 使用 fetch + ReadableStream 实现类 SSE 流式数据接收。
 * 不使用浏览器原生 EventSource，因为需要通过 POST 发送请求体并携带 JWT Token。
 * 组件卸载时自动断开连接，防止内存泄漏。
 */
import { ref, onUnmounted } from 'vue'
import type { SSEMessageData, SSEError } from '@/types/sse'

/** SSE 连接配置 */
export interface SSEOptions {
  url: string
  body: Record<string, unknown>
  headers?: Record<string, string>
  onMessage?: (data: SSEMessageData) => void
  onError?: (error: SSEError) => void
  onDone?: () => void
}

export function useSSE() {
  const isConnected = ref(false)
  let abortController: AbortController | null = null

  /** 建立 SSE 连接，逐行解析 data: 前缀的事件流 */
  async function connect(options: SSEOptions) {
    abortController = new AbortController()
    isConnected.value = true
    let doneCalled = false

    function finalize() {
      if (doneCalled) return
      doneCalled = true
      options.onDone?.()
    }

    const token = localStorage.getItem('token')
    try {
      const response = await fetch(options.url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...(token ? { Authorization: `Bearer ${token}` } : {}),
          ...options.headers,
        },
        body: JSON.stringify(options.body),
        signal: abortController.signal,
      })

      if (!response.ok) {
        // 尝试解析后端返回的错误详情
        // Try to parse error detail from backend response
        const errorText = await response.text()
        let detail = response.statusText
        try { detail = JSON.parse(errorText)?.detail || detail } catch {}
        throw new Error(`HTTP ${response.status}: ${detail}`)
      }

      const reader = response.body?.getReader()
      if (!reader) throw new Error('No response body reader')

      const decoder = new TextDecoder()
      let buffer = ''
      // 当前 SSE 事件的 event 字段，用于区分不同类型的事件
      // Current SSE event type field, used to distinguish different event types
      let currentEvent = ''

      while (true) {
        const { done, value } = await reader.read()
        if (done) break

        buffer += decoder.decode(value, { stream: true })
        const lines = buffer.split('\n')
        buffer = lines.pop() || ''

        for (const line of lines) {
          // 解析 SSE event: 字段，记录当前事件类型
          // Parse SSE event: field, track current event type
          if (line.startsWith('event: ')) {
            currentEvent = line.slice(7).trim()
            // 收到 done 事件时立即结束
            // Finalize immediately on done event
            if (currentEvent === 'done') {
              finalize()
            }
            continue
          }
          if (line.startsWith('data: ')) {
            const dataStr = line.slice(6).trim()
            if (dataStr === '[DONE]') {
              finalize()
              break
            }
            try {
              const data = JSON.parse(dataStr)
              // 将解析到的 event 类型附加到消息对象中
              // Attach parsed event type to message object
              if (currentEvent) {
                data._event = currentEvent
              }
              options.onMessage?.(data)
            } catch {
              // non-JSON data, pass as text
              options.onMessage?.({ type: 'text', content: dataStr, _event: currentEvent || undefined })
            }
            // 每条 data 处理完后重置事件类型（SSE 规范：空行分隔事件）
            // Reset event type after processing data (SSE spec: blank line separates events)
            currentEvent = ''
          } else if (line === '') {
            // SSE 空行表示事件结束，重置事件类型
            // SSE blank line marks event boundary, reset event type
            currentEvent = ''
          }
        }
      }
    } catch (err: unknown) {
      const error = err as SSEError
      if (error.name !== 'AbortError') {
        options.onError?.(error)
      }
      // C14: AbortError (disconnect/unmount) must NOT trigger onDone.
      // onDone only fires for natural stream completion.
    } finally {
      isConnected.value = false
      // Only call finalize (which calls onDone) if NOT aborted.
      // If the stream ended naturally, finalize was already called
      // inside the read loop. The doneCalled guard prevents double-fire.
      if (abortController && !abortController.signal.aborted) {
        finalize()
      }
    }
  }

  function disconnect() {
    abortController?.abort()
    abortController = null
    isConnected.value = false
  }

  onUnmounted(() => {
    disconnect()
  })

  return { isConnected, connect, disconnect }
}
