import request from '@/utils/request'
import { joinApiUrl } from '@/config'
import { getToken } from '@/utils/auth'

const BASE = '/app/consult/ai'

/** 会话列表（含 disclaimer） */
export function listAiConsultSessions(query) {
  return request({
    url: BASE + '/session/list',
    method: 'GET',
    data: query
  })
}

/** 新建会话，body 可选 { category: 1|2|3|4 } */
export function createAiConsultSession(data) {
  return request({
    url: BASE + '/session',
    method: 'POST',
    header: { repeatSubmit: false },
    data: data || {}
  })
}

/**
 * 历史消息（路径参数 sessionId = 若依问诊会话主键，与改造前一致，勿传大模型 sessionId）
 */
export function listAiConsultMessages(sessionId, query) {
  return request({
    url: BASE + '/session/' + sessionId + '/messages',
    method: 'GET',
    data: query
  })
}

/** 提问（服务端同步等待 AI，约 60s） */
export function sendAiConsultMessage(sessionId, data) {
  return request({
    url: BASE + '/session/' + sessionId + '/message',
    method: 'POST',
    header: { repeatSubmit: false },
    data,
    timeout: 65000
  })
}

/** 隐藏/删除会话 */
export function hideAiConsultSession(sessionId) {
  return request({
    url: BASE + '/session/' + sessionId + '/hide',
    method: 'POST',
    header: { repeatSubmit: false }
  })
}

export function getModelList() {
  return request({
    url: '/v1/models',
    method: 'GET'
  })
}

/** 从大模型 SSE/JSON 解析会话 id；流式块中的 id 即为 sessionId */
export function extractLlmSessionIdFromJson(json) {
  if (!json || typeof json !== 'object') {
    return null
  }
  if (json.id != null && json.id !== '') {
    return String(json.id)
  }
  if (json.session_id != null && json.session_id !== '') {
    return String(json.session_id)
  }
  if (json.sessionId != null && json.sessionId !== '') {
    return String(json.sessionId)
  }
  return null
}

function extractDeltaFromChatJson(json) {
  if (!json || typeof json !== 'object') {
    return ''
  }
  const choice = json.choices && json.choices[0]
  if (!choice) {
    return ''
  }
  if (choice.delta && choice.delta.content != null) {
    return String(choice.delta.content)
  }
  if (choice.message && choice.message.content != null) {
    return String(choice.message.content)
  }
  if (choice.text != null) {
    return String(choice.text)
  }
  return ''
}

function parseChatJsonPayload(payload) {
  if (!payload || payload === '[DONE]') {
    return { done: true }
  }
  try {
    const json = JSON.parse(payload)
    const sessionId = extractLlmSessionIdFromJson(json)
    const delta = extractDeltaFromChatJson(json)
    const model =
      json.model != null && json.model !== '' ? String(json.model) : null
    return { sessionId, delta, model }
  } catch (e) {
    return null
  }
}

function parseChatSseDataLine(line) {
  const trimmed = (line || '').trim()
  if (!trimmed || trimmed.startsWith(':')) {
    return null
  }
  if (trimmed.startsWith('data:')) {
    return parseChatJsonPayload(trimmed.slice(5).trim())
  }
  if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
    return parseChatJsonPayload(trimmed)
  }
  return null
}

/** 追加 delta；若上游发的是「累计全文」则覆盖而非重复拼接 */
function appendStreamDelta(state, delta) {
  if (!delta) {
    return
  }
  const prev = state.fullText || ''
  if (!prev) {
    state.fullText = delta
    return
  }
  if (delta === prev) {
    return
  }
  if (delta.startsWith(prev)) {
    state.fullText = delta
    return
  }
  if (prev.startsWith(delta)) {
    return
  }
  state.fullText += delta
}

function consumeSseLines(state, lines, callbacks) {
  for (const line of lines) {
    const parsed = parseChatSseDataLine(line)
    if (!parsed) {
      continue
    }
    if (parsed.sessionId) {
      const changed = state.sessionId !== parsed.sessionId
      state.sessionId = parsed.sessionId
      if (changed && callbacks.onSessionId) {
        callbacks.onSessionId(parsed.sessionId)
      }
    }
    if (parsed.model) {
      state.model = parsed.model
      if (callbacks.onModel) {
        callbacks.onModel(parsed.model)
      }
    }
    if (parsed.delta) {
      const before = state.fullText
      appendStreamDelta(state, parsed.delta)
      if (state.fullText !== before && callbacks.onDelta) {
        callbacks.onDelta(state.fullText, parsed.delta)
      }
    }
  }
}

function appendSseChunk(state, chunk, callbacks) {
  if (!chunk) {
    return
  }
  const text = String(chunk)
  if (!state.buffer && text.trim().startsWith('{') && text.includes('"choices"')) {
    const parsed = parseChatJsonPayload(text.trim())
    if (parsed && parsed.delta) {
      const before = state.fullText
      appendStreamDelta(state, parsed.delta)
      if (state.fullText !== before && callbacks.onDelta) {
        callbacks.onDelta(state.fullText, parsed.delta)
      }
      if (parsed.sessionId) {
        const changed = state.sessionId !== parsed.sessionId
        state.sessionId = parsed.sessionId
        if (changed && callbacks.onSessionId) {
          callbacks.onSessionId(parsed.sessionId)
        }
      }
      if (parsed.model) {
        state.model = parsed.model
        if (callbacks.onModel) {
          callbacks.onModel(parsed.model)
        }
      }
      return
    }
  }
  state.buffer += text
  const lines = state.buffer.split(/\r?\n/)
  state.buffer = lines.pop() || ''
  consumeSseLines(state, lines, callbacks)
}

function flushSseBuffer(state, callbacks) {
  if (!state.buffer) {
    return
  }
  const tail = state.buffer
  state.buffer = ''
  consumeSseLines(state, [tail], callbacks)
}

function decodeResponseChunk(data) {
  if (data == null) {
    return ''
  }
  if (typeof data === 'string') {
    return data
  }
  if (data instanceof ArrayBuffer) {
    try {
      return new TextDecoder('utf-8').decode(data)
    } catch (e) {
      const u8 = new Uint8Array(data)
      let s = ''
      for (let i = 0; i < u8.length; i++) {
        s += String.fromCharCode(u8[i])
      }
      try {
        return decodeURIComponent(escape(s))
      } catch (e2) {
        return s
      }
    }
  }
  return String(data)
}

function streamRequestHeaders() {
  const header = {
    'Content-Type': 'application/json',
    Accept: 'text/event-stream',
    'Cache-Control': 'no-cache'
  }
  const token = getToken()
  if (token) {
    header.Authorization = 'Bearer ' + token
  }
  return header
}

function createStreamState(options) {
  const { onDelta, onSessionId, onModel } = options
  return {
    startMs: Date.now(),
    buffer: '',
    fullText: '',
    sessionId: null,
    model: null,
    chunkReceived: false,
    callbacks: { onDelta, onSessionId, onModel }
  }
}

function finishStreamState(state) {
  flushSseBuffer(state, state.callbacks)
  return {
    content: state.fullText,
    sessionId: state.sessionId,
    model: state.model,
    durationMs: Date.now() - state.startMs
  }
}

// #ifdef H5
async function sendChatMessageStream(body, options) {
  const state = createStreamState(options)
  const res = await fetch(joinApiUrl('/v1/chat/completions'), {
    method: 'POST',
    headers: streamRequestHeaders(),
    body: JSON.stringify(body),
    signal: options.signal
  })
  if (!res.ok) {
    const text = await res.text()
    let msg = 'requestFailed'
    try {
      const err = JSON.parse(text)
      msg = (err && (err.message || err.msg)) || msg
    } catch (e) {
      if (text) {
        msg = text.slice(0, 200)
      }
    }
    throw new Error(msg)
  }
  const reader = res.body && res.body.getReader()
  if (!reader) {
    throw new Error('requestFailed')
  }
  const decoder = new TextDecoder()
  while (true) {
    const { done, value } = await reader.read()
    if (done) {
      break
    }
    state.chunkReceived = true
    appendSseChunk(state, decoder.decode(value, { stream: true }), state.callbacks)
  }
  appendSseChunk(state, decoder.decode(), state.callbacks)
  return finishStreamState(state)
}
// #endif

// #ifndef H5
function sendChatMessageStream(body, options) {
  const state = createStreamState(options)
  const url = joinApiUrl('/v1/chat/completions')

  return new Promise((resolve, reject) => {
    const reqOptions = {
      url,
      method: 'POST',
      header: streamRequestHeaders(),
      data: body,
      timeout: 600000,
      enableChunked: true,
      responseType: 'text',
      success(res) {
        if (!res || res.statusCode < 200 || res.statusCode >= 300) {
          let msg = 'requestFailed'
          try {
            const errBody = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
            msg = (errBody && (errBody.message || errBody.msg)) || msg
          } catch (e) {
            /* ignore */
          }
          reject(new Error(msg))
          return
        }
        if (!state.chunkReceived && res.data) {
          appendSseChunk(state, decodeResponseChunk(res.data), state.callbacks)
        }
        resolve(finishStreamState(state))
      },
      fail(err) {
        reject(err || new Error('requestFailed'))
      }
    }

    const task = uni.request(reqOptions)
    if (options.onReadyTask && task) {
      options.onReadyTask(task)
    }
    if (task && typeof task.onChunkReceived === 'function') {
      task.onChunkReceived((res) => {
        state.chunkReceived = true
        appendSseChunk(state, decodeResponseChunk(res.data), state.callbacks)
      })
    }
  })
}
// #endif

/**
 * 聊天 POST /v1/chat/completions（默认 stream: true）
 * options: onDelta, onSessionId, onModel, onReadyTask(task), signal(H5)
 * 流式 resolve：{ content, sessionId, model, durationMs }，durationMs 为整段流式请求耗时（毫秒）
 */
export function sendChatMessage(data, options = {}) {
  const body = { stream: true, ...(data || {}) }
  if (body.stream !== false) {
    return sendChatMessageStream(body, options)
  }
  return request({
    url: '/v1/chat/completions',
    method: 'POST',
    data: body,
    timeout: 600000
  })
}
