/**
 * SSE 工具函数 — 为阶段二/三的流式功能做准备
 *
 * 阶段二将用于：
 *   1. 问题排查 AI 分析流 /api/analyze/stream
 *      事件类型：start / matched_cases / chunk / done / error
 *   2. 服务监测巡检流 /api/service-monitor/run/stream
 *      事件类型：progress / module_done / finished / error / cancelled
 *
 * 后端响应头：Cache-Control: no-cache, X-Accel-Buffering: no, Connection: keep-alive
 *
 * 断线重连（P3-2）：
 *   - 连接断开后自动重连，递增退避 1s→2s→4s→8s，最多 3 次
 *   - 重连成功后新内容追加到已有内容，不重置
 *   - 用户手动 close() 后不自动重连
 *   - 3 次重试失败判定最终失败，回调 onError
 */

export interface SSEOptions {
  /** SSE 端点 URL（相对路径，如 /api/analyze/stream） */
  url: string
  /** URL 查询参数 */
  params?: Record<string, string>
  /** 事件处理器映射（key 为事件 type） */
  handlers: Record<string, (data: any) => void>
  /** 连接错误回调（所有重连尝试均失败后触发） */
  onError?: (error: Event) => void
  /** 连接打开回调 */
  onOpen?: () => void
  /** 是否携带 Cookie（默认 true，Flask Session 需要） */
  withCredentials?: boolean
  /** onerror 最大连续触发次数，超过后才回调 onError（默认 3，避免短暂波动误判） */
  maxErrorCount?: number
}

export interface SSEConnection {
  /** 关闭 SSE 连接 */
  close: () => void
  /** 获取原生 EventSource 实例（高级用法） */
  getSource: () => EventSource | null
}

/**
 * 创建通用 SSE 连接（支持断线自动重连）
 *
 * 断线重连策略：
 *   - 递增退避：1s → 2s → 4s → 8s
 *   - 最多重试 3 次
 *   - 重连成功后续内容追加到已有内容（不重置）
 *   - 手动 close() 后不自动重连
 *   - 3 次重试失败后回调 onError
 *
 * 用法示例（阶段二）：
 *   const conn = createSSE({
 *     url: '/api/analyze/stream',
 *     params: { query: '...', project_name: '...' },
 *     handlers: {
 *       start: (e) => console.log('开始', e.message),
 *       chunk: (e) => appendContent(e.content),
 *       done: (e) => console.log('完成', e.api_time),
 *       error: (e) => console.error('错误', e.message),
 *     }
 *   })
 *   // 需要取消时
 *   conn.close()
 */
export function createSSE(options: SSEOptions): SSEConnection {
  const {
    url,
    params = {},
    handlers,
    onError,
    onOpen,
    withCredentials = true,
  } = options

  // 构建带参数的 URL
  const queryString = new URLSearchParams(params).toString()
  const fullUrl = queryString ? `${url}?${queryString}` : url

  let source: EventSource | null = null
  let closed = false
  let reconnectAttempts = 0
  const maxReconnectAttempts = 3

  function connect() {
    if (closed) return

    source = new EventSource(fullUrl, { withCredentials })

    // 连接打开（首次连接 + 重连成功均触发）
    source.onopen = () => {
      reconnectAttempts = 0  // 重连成功，重置重试计数
      console.log(`SSE 连接已建立` + (reconnectAttempts > 0 ? '（重连成功）' : ''))
      if (onOpen) {
        onOpen()
      }
    }

    // 监听消息
    source.onmessage = (event) => {
      reconnectAttempts = 0  // 收到正常消息，重置重试计数
      try {
        const data = JSON.parse(event.data)
        const eventType = data.event || data.type
        const handler = handlers[eventType]
        if (handler) {
          handler(data)
        }
      } catch (err) {
        console.warn('SSE 消息解析失败:', event.data, err)
      }
    }

    // 监听错误（触发断线重连）
    source.onerror = (_event: Event) => {
      if (closed) return

      source?.close()

      reconnectAttempts++
      if (reconnectAttempts <= maxReconnectAttempts) {
        const delay = Math.min(1000 * Math.pow(2, reconnectAttempts - 1), 8000)
        console.warn(
          `SSE 连接断开，${delay / 1000}s 后第 ${reconnectAttempts} 次重连...`
        )
        setTimeout(() => connect(), delay)
      } else {
        // 3 次重连均失败，判定最终失败
        console.error(`SSE 连接失败，已重试 ${maxReconnectAttempts} 次`)
        closed = true
        if (onError) {
          onError(_event)
        }
      }
    }
  }

  // 建立首次连接
  connect()

  return {
    close: () => {
      closed = true
      if (source) {
        source.close()
        source = null
      }
    },
    getSource: () => source,
  }
}

/**
 * 创建巡检 SSE 连接（服务监测专用）
 *
 * 事件类型：progress / module_done / finished / error / cancelled
 * 阶段三实现具体逻辑时使用
 */
export function createInspectionSSE(
  targetId: string,
  suite: string,
  handlers: Record<string, (data: any) => void>,
  onError?: (error: Event) => void
): SSEConnection {
  return createSSE({
    url: '/api/service-monitor/run/stream',
    params: { target_id: targetId, suite },
    handlers,
    onError,
  })
}