import http from './http'

/**
 * AI 助手 API：会话/工具走 axios（带鉴权拦截器）；对话走原生 fetch 流式（SSE），
 * 因为 EventSource 只能 GET、且无法设置 Authorization header。fetch 这里手动对齐
 * axios 的双轨鉴权（Cookie + Bearer + X-Scope）。后端见 SimpleLite `Web/Assistant/AssistantApi.cs`。
 */

const API_BASE = (import.meta.env.VITE_API_BASE as string | undefined) ?? '/api'
const REST = '/sl/projection/assistant'

export interface AssistantSessionMeta {
  id: string
  title: string
  created: string
  updated: string
  turns: number
}

export interface AssistantToolMeta {
  name: string
  description: string
  isWrite: boolean
}

export interface AssistantHistoryTurn {
  role: 'user' | 'assistant' | 'tool'
  content?: string | null
  toolCalls?: { name: string; arguments: string }[]
  time: string
}

export interface AssistantHistory {
  id: string
  title: string
  created: string
  updated: string
  turns: AssistantHistoryTurn[]
}

export async function listSessions(): Promise<AssistantSessionMeta[]> {
  const { data } = await http.get<AssistantSessionMeta[]>(`${REST}/sessions`)
  return data ?? []
}

export async function getHistory(sessionId: string): Promise<AssistantHistory> {
  const { data } = await http.get<AssistantHistory>(`${REST}/history`, { params: { sessionId } })
  return data
}

export async function createSession(title?: string): Promise<{ id: string; title: string }> {
  const { data } = await http.post<{ id: string; title: string }>(
    `${REST}/sessions`,
    null,
    title ? { params: { title } } : undefined
  )
  return data
}

export async function deleteSession(id: string): Promise<void> {
  await http.delete(`${REST}/sessions/${encodeURIComponent(id)}`)
}

export async function listTools(): Promise<AssistantToolMeta[]> {
  const { data } = await http.get<AssistantToolMeta[]>(`${REST}/tools`)
  return data ?? []
}

export interface AssistantStreamHandlers {
  onSession?: (sessionId: string) => void
  onToken?: (delta: string) => void
  onToolCall?: (name: string, args: unknown) => void
  onToolResult?: (name: string, ok: boolean, result: unknown) => void
  onError?: (message: string) => void
  onDone?: (finishReason: string, usedTools: number) => void
}

/**
 * 发起一轮对话并以 SSE 流式消费。通过 <paramref name="signal"/> 支持中断（停止生成）。
 * 事件协议见后端 §6.2：session / token / tool_call / tool_result / error / done。
 */
export async function streamChat(
  payload: { message: string; sessionId?: string | null; profile?: string },
  handlers: AssistantStreamHandlers,
  signal: AbortSignal
): Promise<void> {
  const token = localStorage.getItem('simple.auth.token')
  const scope = localStorage.getItem('simple.auth.scope')

  let resp: Response
  try {
    resp = await fetch(`${API_BASE}${REST}/chat`, {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
        Accept: 'text/event-stream',
        ...(token ? { Authorization: `Bearer ${token}` } : {}),
        ...(scope ? { 'X-Scope': scope } : {})
      },
      body: JSON.stringify({
        message: payload.message,
        sessionId: payload.sessionId ?? undefined,
        profile: payload.profile ?? 'analysis'
      }),
      signal
    })
  } catch (e) {
    if ((e as Error).name === 'AbortError') return
    handlers.onError?.('网络错误：无法连接 AI 助手服务。')
    handlers.onDone?.('error', 0)
    return
  }

  if (resp.status === 401) {
    handlers.onError?.('登录已失效，请重新登录。')
    handlers.onDone?.('error', 0)
    return
  }
  if (!resp.ok || !resp.body) {
    let msg = `请求失败（HTTP ${resp.status}）`
    if (resp.status === 403) msg = '无权使用 AI 助手（需要 Platform 权限）。'
    else if (resp.status === 502 || resp.status === 504) msg = 'SimpleLite 未连接：请先启动后端（端口 8222）。'
    else {
      try {
        const t = await resp.text()
        if (t) msg = t.slice(0, 500)
      } catch {
        /* ignore */
      }
    }
    handlers.onError?.(msg)
    handlers.onDone?.('error', 0)
    return
  }

  const reader = resp.body.getReader()
  const decoder = new TextDecoder()
  let buf = ''
  try {
    for (;;) {
      const { value, done } = await reader.read()
      if (done) break
      buf += decoder.decode(value, { stream: true })
      let sep: number
      // 帧之间以空行（\n\n）分隔。
      while ((sep = indexOfFrameBoundary(buf)) >= 0) {
        const frame = buf.slice(0, sep)
        buf = buf.slice(sep).replace(/^(\r?\n){2}/, '')
        dispatchFrame(frame, handlers)
      }
    }
    if (buf.trim()) dispatchFrame(buf, handlers)
  } catch (e) {
    if ((e as Error).name !== 'AbortError') {
      handlers.onError?.((e as Error).message || '读取流失败')
      handlers.onDone?.('error', 0)
    }
  }
}

function indexOfFrameBoundary(s: string): number {
  const a = s.indexOf('\n\n')
  const b = s.indexOf('\r\n\r\n')
  if (a < 0) return b
  if (b < 0) return a
  return Math.min(a, b)
}

function dispatchFrame(raw: string, h: AssistantStreamHandlers): void {
  let event = 'message'
  const dataLines: string[] = []
  for (const lineRaw of raw.split('\n')) {
    const line = lineRaw.replace(/\r$/, '')
    if (line.startsWith('event:')) event = line.slice(6).trim()
    else if (line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /, ''))
  }
  const dataStr = dataLines.join('\n')
  let data: Record<string, unknown> = {}
  try {
    data = dataStr ? JSON.parse(dataStr) : {}
  } catch {
    data = { raw: dataStr }
  }

  switch (event) {
    case 'session':
      h.onSession?.(String(data.sessionId ?? ''))
      break
    case 'token':
      h.onToken?.(String(data.delta ?? ''))
      break
    case 'tool_call':
      h.onToolCall?.(String(data.name ?? ''), data.args)
      break
    case 'tool_result':
      h.onToolResult?.(String(data.name ?? ''), Boolean(data.ok), data.result)
      break
    case 'error':
      h.onError?.(String(data.message ?? '未知错误'))
      break
    case 'done':
      h.onDone?.(String(data.finishReason ?? 'stop'), Number(data.usedTools ?? 0))
      break
    default:
      break
  }
}
