import { reactive, readonly, ref } from 'vue'
import { subscribeRunStream, getRunState } from '../api/chat'

/**
 * 全局 Run 池。
 *
 * <p>对应设计文档 {@code docs/design/agent-chat-page-design.md} §5.7。
 * 解耦「哪个 run 在跑」与「哪个 run 在看」：</p>
 * <ul>
 *   <li>多个会话可同时开 run，互不干扰；每个 run 独立维护 SSE 连接 + 事件缓冲</li>
 *   <li>视图层通过 {@link attachView} 订阅某个 run 的事件流，切换会话时仅切换视图，
 *       不中断后台 run</li>
 *   <li>断线重连由池内自动管理，视图层无感</li>
 * </ul>
 *
 * <p>当前实现：单例（模块级单例），所有会话共享一个 reactive Map。</p>
 */

/** 单个 run 的运行态 */
function createRunEntry(runId, sessionId, assistantMessageId) {
  return {
    runId,
    sessionId,
    assistantMessageId,
    status: 'RUNNING', // RUNNING / SUCCESS / FAILED / UNKNOWN
    /** 收到的事件流（按时间顺序） */
    events: [],
    /** 当前最大事件 id（断线重连时作为 Last-Event-ID） */
    lastEventId: null,
    /** 是否正在重连 */
    reconnecting: false,
    /** 重连失败计数 */
    reconnectAttempts: 0,
    /** 当前未答 clarify 暂停点（null 表示无） */
    pendingAsk: null,
    /** 内部：视图回调（attachView 时设置，新事件来时调用） */
    _viewCallback: null,
    /** 内部：AbortController（fetch SSE 用），便于主动断开 */
    _abortCtrl: null,
    /** 内部：重连定时器 id */
    _reconnectTimer: null
  }
}

const MAX_RECONNECT_ATTEMPTS = 5
const RECONNECT_DELAY_MS = 3000

// 模块级单例：所有组件共享一份状态
const runs = reactive({}) // runId -> runEntry

/** 当前视图附加到的 runId（null 表示未附加） */
const activeViewRunId = ref(null)

/**
 * 注册一个 run，并立即开始订阅 SSE 流。
 *
 * @param {string} runId
 * @param {number} sessionId
 * @param {number} assistantMessageId
 */
function register(runId, sessionId, assistantMessageId) {
  if (runs[runId]) {
    return runs[runId] // 已存在，幂等返回
  }
  const entry = createRunEntry(runId, sessionId, assistantMessageId)
  runs[runId] = entry
  // 异步订阅，不阻塞调用方
  consumeSSE(runId)
  return entry
}

/**
 * 视图附加：订阅某个 run 的事件流。
 * 切换会话时，调用 attachView(newRunId) 即可，原 run 继续后台运行。
 *
 * @param {string|null} runId  null 表示脱离
 * @param {Function} onUpdate 可选，每次有新事件时回调
 */
function attachView(runId, onUpdate) {
  activeViewRunId.value = runId
  if (runId && onUpdate) {
    const entry = runs[runId]
    if (entry) {
      // 保存 callback：parseFrame 每收到新事件都会调 entry._viewCallback
      entry._viewCallback = onUpdate
      // 立即把已有事件回放给视图（断线重连/路由切回来场景）
      entry.events.forEach(evt => onUpdate(evt, entry))
    }
  }
}

/**
 * 主动取消订阅（不再需要该 run 的事件，例如会话被删除）。
 */
function unregister(runId) {
  const entry = runs[runId]
  if (!entry) return
  if (entry._abortCtrl) {
    // abort() 会导致 consumeSSE 内 await reader.read() 抛 AbortError，由 consumeSSE 的 catch 处理；
    // 此处 try/catch 仅防御 abort() 本身在已完成 controller 上抛错（属预期，无需上报）
    try { entry._abortCtrl.abort() } catch (_) { /* AbortError expected */ }
  }
  if (entry._reconnectTimer) {
    clearTimeout(entry._reconnectTimer)
  }
  delete runs[runId]
  if (activeViewRunId.value === runId) {
    activeViewRunId.value = null
  }
}

/**
 * 清除某个 run 的 pendingAsk（用户提交 clarify 答案后调用）。
 *
 * <p>因为对外暴露的是 {@link readonly} 视图，外部不能直接修改 {@code entry.pendingAsk}；
 * 这个内部方法绕开 readonly，让 useChat 能在提交答案后立即清除本地提示。</p>
 */
function clearPendingAsk(runId) {
  const entry = runs[runId]
  if (entry) {
    entry.pendingAsk = null
  }
}

/**
 * 清理所有非 RUNNING 的 run（组件卸载时调用，避免内存泄漏）。
 *
 * <p>RUNNING 中的 run 不清理：用户可能切走路由后回来还想看到结果，
 * 这些 run 由后端 SseEventBus 24h 超时兜底；其他状态（SUCCESS/FAILED/UNKNOWN）
 * 已无意义留在内存里。</p>
 */
function cleanupFinished() {
  for (const runId of Object.keys(runs)) {
    const entry = runs[runId]
    if (entry && entry.status !== 'RUNNING') {
      unregister(runId)
    }
  }
}

/**
 * 硬重置：清空所有 run（含 RUNNING），断开所有 SSE 连接。
 *
 * <p>与 {@link cleanupFinished} 的区别：本方法会强制中断运行中的 run，主要用于：</p>
 * <ul>
 *   <li>测试场景下重置模块单例，避免用例之间状态串扰</li>
 *   <li>未来若引入登录/账号切换，登出时调用以避免上一次会话的数据泄漏到下一次</li>
 *   <li>用户主动"放弃所有运行"等管理操作</li>
 * </ul>
 * <p>当前项目无登录体系，本方法仅作为基础设施暴露；调用时机由调用方决定。</p>
 */
function reset() {
  for (const runId of Object.keys(runs)) {
    unregister(runId)
  }
  activeViewRunId.value = null
}

/** 查询某个 run 的当前状态（只读视图） */
function getRun(runId) {
  return runs[runId] || null
}

/** 列出所有 run id（用于侧栏显示 loading 指示） */
function listRunIds() {
  return Object.keys(runs)
}

/** 该 session 是否有正在运行的 run（侧栏 loading 指示用） */
function hasActiveRunForSession(sessionId) {
  return Object.values(runs).some(r => r.sessionId === sessionId && r.status === 'RUNNING')
}

/* ============== 内部：SSE 流消费 ============== */

async function consumeSSE(runId) {
  const entry = runs[runId]
  if (!entry) return
  entry._abortCtrl = new AbortController()
  try {
    const resp = await subscribeRunStream(runId, entry.lastEventId)
    if (!resp.ok || !resp.body) {
      throw new Error(`HTTP ${resp.status}`)
    }
    entry.reconnecting = false
    entry.reconnectAttempts = 0
    const reader = resp.body.getReader()
    const decoder = new TextDecoder('utf-8')
    let buffer = ''
    while (true) {
      const { value, done } = await reader.read()
      if (done) break
      buffer += decoder.decode(value, { stream: true })
      // 按 \n\n 切分 SSE 帧
      const frames = buffer.split('\n\n')
      buffer = frames.pop() // 最后一帧可能不完整，保留
      for (const frame of frames) {
        parseFrame(runId, frame)
      }
    }
    // 流正常结束：通过 GET /state 确认最终状态
    await refreshRunState(runId)
  } catch (e) {
    console.warn('[useGlobalRuns] SSE 中断', runId, e.message)
    scheduleReconnect(runId)
  }
}

function parseFrame(runId, frame) {
  const entry = runs[runId]
  if (!entry) return
  let eventName = 'message'
  let dataStr = ''
  let eventId = null
  for (const line of frame.split('\n')) {
    if (line.startsWith('id:')) {
      eventId = parseInt(line.slice(3).trim(), 10)
      if (!Number.isNaN(eventId)) {
        entry.lastEventId = eventId
      }
    } else if (line.startsWith('event:')) {
      eventName = line.slice(6).trim()
    } else if (line.startsWith('data:')) {
      dataStr += line.slice(5).trim()
    }
    // 心跳注释行（:keep-alive）忽略
  }
  if (!dataStr) return
  let payload = null
  try { payload = JSON.parse(dataStr) } catch (_) { payload = dataStr }
  const evt = { id: eventId, event: eventName, data: payload, at: Date.now() }
  entry.events.push(evt)
  // 根据事件类型更新状态
  applyEvent(entry, evt)
  // 转发给当前视图（关键：这是实时事件到 UI 的唯一通道）
  if (typeof entry._viewCallback === 'function') {
    try { entry._viewCallback(evt, entry) } catch (e) {
      console.warn('[useGlobalRuns] _viewCallback 异常', runId, e?.message || e)
    }
  }
}

function applyEvent(entry, evt) {
  const { event, data } = evt
  if (event === 'node_stream') {
    // 拦截 clarify_request
    if (data && data.kind === 'clarify_request') {
      entry.pendingAsk = parseClarify(data)
    }
  } else if (event === 'workflow_complete') {
    entry.status = 'SUCCESS'
    entry.pendingAsk = null
  } else if (event === 'workflow_error') {
    entry.status = 'FAILED'
  }
}

function parseClarify(data) {
  // data.content 是 JSON 字符串：{clarify_id, question, choices}
  try {
    const content = typeof data.content === 'string' ? JSON.parse(data.content) : data.content
    return {
      clarifyId: content.clarify_id,
      question: content.question || '',
      choices: Array.isArray(content.choices) ? content.choices : []
    }
  } catch (_) {
    return null
  }
}

function scheduleReconnect(runId) {
  const entry = runs[runId]
  if (!entry) return
  if (entry.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
    // 重连耗尽：退化为 GET /state 单次确认，UI 显示"连接已断开"
    refreshRunState(runId)
    return
  }
  entry.reconnecting = true
  entry.reconnectAttempts++
  entry._reconnectTimer = setTimeout(() => consumeSSE(runId), RECONNECT_DELAY_MS)
}

async function refreshRunState(runId) {
  const entry = runs[runId]
  if (!entry) return
  try {
    const state = await getRunState(runId)
    if (state && state.data) {
      entry.status = state.data.status || entry.status
      if (state.data.currentPause) {
        entry.pendingAsk = {
          clarifyId: state.data.currentPause.clarifyId,
          question: state.data.currentPause.question || '',
          choices: state.data.currentPause.choices
            ? (typeof state.data.currentPause.choices === 'string'
                ? JSON.parse(state.data.currentPause.choices)
                : state.data.currentPause.choices)
            : []
        }
      } else {
        entry.pendingAsk = null
      }
    }
  } catch (e) {
    console.warn('[useGlobalRuns] refreshRunState 失败', runId, e.message)
  }
}

/**
 * 暴露给组件的 API（只读状态 + 操作方法）。
 *
 * 用法：
 * ```js
 * const globalRuns = useGlobalRuns()
 * globalRuns.register(runId, sessionId, msgId)
 * globalRuns.attachView(runId, (evt, entry) => { ... })
 * ```
 */
export function useGlobalRuns() {
  return {
    runs: readonly(runs),
    activeViewRunId: readonly(activeViewRunId),
    register,
    unregister,
    attachView,
    getRun,
    listRunIds,
    hasActiveRunForSession,
    clearPendingAsk,
    cleanupFinished,
    reset
  }
}
