import { mapQoderCloudEvent } from './runtime-events.js'

export const QODER_CLOUD_AGENT_CODE = 'qoder-cloud-agent'
export const QODER_CLOUD_AGENT_PROTOCOL = 'qoder-cloud-agent'

export function isQoderCloudAgentReady(row = {}) {
  return !!(
    row.enabled &&
    String(row.api_key || '').trim() &&
    String(row.runtime_id || '').trim() &&
    String(row.endpoint_id || '').trim()
  )
}

export function missingQoderCloudAgentConfig(row = {}) {
  const missing = []
  if (!String(row.api_key || '').trim()) missing.push('api_key')
  if (!String(row.runtime_id || '').trim()) missing.push('environment_id')
  if (!String(row.endpoint_id || '').trim()) missing.push('agent_id')
  return missing
}

export function secretPreview(value = '') {
  const s = String(value || '')
  if (!s) return null
  if (s.length <= 8) return '****'
  return `${s.slice(0, 4)}****${s.slice(-4)}`
}

export function rowToQoderCloudAgentRuntime(row = {}) {
  return {
    code: row.code,
    name: row.name,
    icon: row.icon || 'Q',
    protocol: row.protocol,
    enabled: !!row.enabled,
    isActive: !!row.is_active,
    hasKey: !!row.api_key,
    keyPreview: secretPreview(row.api_key),
    hasInvocationKey: !!row.api_key,
    invocationKeyPreview: secretPreview(row.api_key),
    runtimeId: row.runtime_id || '',
    endpointId: row.endpoint_id || '',
    runtimeName: row.runtime_name || '',
    endpointName: row.endpoint_name || '',
    environmentId: row.runtime_id || '',
    agentId: row.endpoint_id || '',
    baseUrl: row.endpoint_public_url || '',
    endpointPublicUrl: row.endpoint_public_url || '',
    purposeScopes: safeJsonArray(row.purpose_scopes_json, ['create', 'evaluate', 'run']),
    timeoutMs: Number(row.timeout_ms || 300000),
    notes: row.notes || '',
    missing: missingQoderCloudAgentConfig(row),
    ready: isQoderCloudAgentReady(row),
  }
}

export function sanitizeQoderCloudAgentPatch(body = {}, existing = {}) {
  const patch = {}
  const copyString = (field, column) => {
    if (typeof body[field] === 'string') {
      const value = body[field].trim()
      if (value || existing[column]) patch[column] = value
    }
  }
  copyString('name', 'name')
  copyString('icon', 'icon')
  if (typeof body.invocationKey === 'string' && body.invocationKey.trim()) patch.api_key = body.invocationKey.trim()
  if (typeof body.apiKey === 'string' && body.apiKey.trim()) patch.api_key = body.apiKey.trim()
  copyString('environmentId', 'runtime_id')
  copyString('runtimeId', 'runtime_id')
  copyString('agentId', 'endpoint_id')
  copyString('endpointId', 'endpoint_id')
  copyString('baseUrl', 'endpoint_public_url')
  copyString('endpointPublicUrl', 'endpoint_public_url')
  copyString('notes', 'notes')
  if (typeof body.timeoutMs === 'number' || typeof body.timeoutMs === 'string') {
    patch.timeout_ms = Math.max(5000, Math.min(60 * 60 * 1000, Number(body.timeoutMs) || 300000))
  }
  if (Array.isArray(body.purposeScopes)) {
    patch.purpose_scopes_json = JSON.stringify(body.purposeScopes.map(String).filter(Boolean))
  }
  if (typeof body.enabled === 'boolean') patch.enabled = body.enabled ? 1 : 0
  return {
    patch,
    merged: { ...existing, ...patch },
  }
}

export function buildQoderCloudPrompt({ systemPrompt, chatMessages, mode, agentPrompt }) {
  const messages = (chatMessages || [])
    .map(m => `${m.role === 'assistant' ? 'Assistant' : 'User'}: ${m.content}`)
    .join('\n\n')
  return [
    '你正在作为 AIPROS 平台的 QODER Cloud Agent 运行时执行任务。',
    `运行场景：${mode || 'run'}`,
    '',
    agentPrompt ? `## 智能体提示词\n${agentPrompt}` : '',
    systemPrompt ? `## 系统/Skill 提示词\n${systemPrompt}` : '',
    '## 对话输入',
    messages,
    '',
    '根据对话直接工作。信息不足时可以先追问；信息足够时继续执行。平台不要求固定输出格式，也不会在生成阶段进行完整性卡控。',
  ].filter(Boolean).join('\n')
}

export async function* runQoderCloudAgent({
  runtimeRow,
  prompt,
  signal,
  providerSessionId = '',
  afterProviderEventId = '',
  aiprosSessionId = '',
}) {
  if (!isQoderCloudAgentReady(runtimeRow)) {
    yield { t: 'error', v: `QODER Cloud Agent 配置未就绪：${missingQoderCloudAgentConfig(runtimeRow).join(', ')}` }
    return
  }

  const abortController = new AbortController()
  if (signal) {
    if (signal.aborted) abortController.abort()
    else signal.addEventListener('abort', () => abortController.abort(), { once: true })
  }

  const apiBase = String(runtimeRow.endpoint_public_url || '').trim().replace(/\/+$/, '') || 'https://api.qoder.com/api/v1/cloud'
  const token = String(runtimeRow.api_key || '').trim()
  const headers = {
    Authorization: `Bearer ${token}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  }
  try {
    let sessionId = String(providerSessionId || '').trim()
    if (!sessionId) {
      const sessionResp = await fetch(`${apiBase}/sessions`, {
        method: 'POST',
        headers,
        body: JSON.stringify({
          agent: String(runtimeRow.endpoint_id || '').trim(),
          environment_id: String(runtimeRow.runtime_id || '').trim(),
          title: 'AIPROS Lite Skill Run',
        }),
        signal: abortController.signal,
      })
      if (!sessionResp.ok) {
        yield { t: 'error', v: `QODER Cloud Agent 创建会话失败：${await readErrorText(sessionResp)}` }
        return
      }
      const sessionData = await sessionResp.json().catch(() => ({}))
      sessionId = sessionData?.id || sessionData?.data?.id
    }
    if (!sessionId) {
      yield { t: 'error', v: 'QODER Cloud Agent 创建会话失败：响应中没有 session id' }
      return
    }
    yield {
      t: 'provider_session',
      provider: QODER_CLOUD_AGENT_CODE,
      providerSessionId: String(sessionId),
      ...(afterProviderEventId ? { providerEventId: String(afterProviderEventId) } : {}),
    }

    const afterQuery = afterProviderEventId ? `?after_id=${encodeURIComponent(afterProviderEventId)}` : ''
    const streamUrl = `${apiBase}/sessions/${encodeURIComponent(sessionId)}/events/stream${afterQuery}`
    const streamPromise = readCloudAgentEventStream({
      url: streamUrl,
      token,
      signal: abortController.signal,
    })

    const eventResp = await fetch(`${apiBase}/sessions/${encodeURIComponent(sessionId)}/events`, {
      method: 'POST',
      headers,
      body: JSON.stringify({
        events: [{
          type: 'user.message',
          content: [{ type: 'text', text: String(prompt || '') }],
        }],
      }),
      signal: abortController.signal,
    })
    if (!eventResp.ok) {
      yield { t: 'error', v: `QODER Cloud Agent 发送消息失败：${await readErrorText(eventResp)}` }
      return
    }

    for await (const evt of streamPromise) {
      if (abortController.signal.aborted) return
      const mapped = mapQoderCloudEvent(evt, { sessionId: aiprosSessionId })
      for (const next of mapped) yield next
      if (mapped.some(next => next.t === 'error' || next.t === 'provider_done')) return
    }
  } catch (e) {
    if (!abortController.signal.aborted) {
      yield { t: 'error', v: `QODER Cloud Agent 执行失败：${e?.message || e}` }
    }
  }
}

async function* readCloudAgentEventStream({ url, token, signal }) {
  const resp = await fetch(url, {
    headers: { Authorization: `Bearer ${token}`, Accept: 'text/event-stream' },
    signal,
  })
  if (!resp.ok) {
    yield { t: 'error', v: `QODER Cloud Agent 事件流连接失败：${await readErrorText(resp)}` }
    return
  }
  if (!resp.body) {
    yield { t: 'error', v: 'QODER Cloud Agent 事件流为空' }
    return
  }

  const reader = resp.body.getReader()
  const decoder = new TextDecoder()
  let buf = ''
  const pending = { data: [] }
  try {
    while (!signal?.aborted) {
      const { value, done } = await reader.read()
      if (done) {
        const evt = consumeSseLine('', pending)
        if (evt) {
          const parsed = parseSseData(evt.data)
          yield { event: parsed?.type || evt.event, id: evt.id, data: parsed }
        }
        break
      }
      buf += decoder.decode(value, { stream: true })
      let idx
      while ((idx = buf.indexOf('\n')) !== -1) {
        const rawLine = buf.slice(0, idx)
        buf = buf.slice(idx + 1)
        const line = rawLine.endsWith('\r') ? rawLine.slice(0, -1) : rawLine
        const evt = consumeSseLine(line, pending)
        if (!evt) continue
        const parsed = parseSseData(evt.data)
        yield {
          event: parsed?.type || evt.event,
          id: evt.id,
          data: parsed,
        }
      }
    }
  } catch (e) {
    if (!signal?.aborted) yield { t: 'error', v: `QODER Cloud Agent 事件流读取失败：${e?.message || e}` }
  } finally {
    try { await reader.cancel().catch(() => {}) } catch {}
  }
}

function consumeSseLine(line, pending) {
  if (line === '') {
    if (!pending.event && !pending.data.length) return null
    const evt = { id: pending.id, event: pending.event || 'message', data: pending.data.join('\n') }
    pending.id = undefined
    pending.event = undefined
    pending.data = []
    return evt
  }
  if (line.startsWith(':')) return null
  const idx = line.indexOf(':')
  const key = idx === -1 ? line : line.slice(0, idx)
  let value = idx === -1 ? '' : line.slice(idx + 1)
  if (value.startsWith('')) value = value.replace(/^ /, '')
  if (key === 'event') pending.event = value
  if (key === 'id') pending.id = value
  if (key === 'data') pending.data.push(value)
  return null
}

function parseSseData(data) {
  try { return JSON.parse(data || '{}') } catch { return { type: 'message', content: data } }
}

async function readErrorText(resp) {
  const text = await resp.text().catch(() => '')
  return `HTTP ${resp.status}${text ? ` - ${text.slice(0, 800)}` : ''}`
}

function safeJsonArray(value, fallback = []) {
  if (Array.isArray(value)) return value.map(String).filter(Boolean)
  try {
    const parsed = JSON.parse(String(value || ''))
    return Array.isArray(parsed) ? parsed.map(String).filter(Boolean) : fallback
  } catch {
    return fallback
  }
}
