import {
  API_URL,
  API_KEY,
  PRO_URL,
  PRO_FRONTEND_TIMEOUT_MS,
  WEB_SEARCH_URL,
  WEB_SEARCH_FRONTEND_TIMEOUT_MS,
  TTS_URL,
  TTS_STATUS_URL,
  TTS_CONFIG_URL,
  COMPRESS_SUBMIT_URL,
  COMPRESS_STATUS_URL,
  JOB_LOAD_URL,
} from './config.js'
import { safeString, stripTrailingSlashes } from './string.js'
import { parseSseEvent } from './sse.js'
import { safeFetch, buildHeaders, parseJsonResponse } from './fetch.js'
import { useSettingsStore } from '../stores/settings'

function generateRequestId() {
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return crypto.randomUUID()
  }
  return `${Date.now()}-${Math.random().toString(16).slice(2)}`
}

function normalizeAbortReason(reason) {
  return safeString(reason).trim().slice(0, 64) || 'abort'
}

function createAbortError(message = 'Request aborted') {
  const error = new Error(message)
  error.name = 'AbortError'
  return error
}

async function sendCancelRequest(cancelUrl, requestId, reason) {
  try {
    const headers = {
      'Content-Type': 'application/json',
    }
    if (API_KEY) {
      headers['X-API-Key'] = API_KEY
    }
    await fetch(cancelUrl, {
      method: 'POST',
      headers,
      credentials: 'include',
      body: JSON.stringify({
        request_id: requestId,
        reason,
      }),
    })
  } catch {
    // Best-effort cancel only
  }
}

const CANCEL_PATH_MAP = {
  '/v1/pro/completions': '/v1/pro/completions/cancel',
  '/v1/web-search': '/v1/web-search/cancel',
  '/v1/completions': '/v1/completions/cancel',
}

function getCancelUrl(apiUrl) {
  const base = stripTrailingSlashes(apiUrl)
  return CANCEL_PATH_MAP[base] || `${base}/cancel`
}

function buildCompletionBody(settings, prefix, suffix, languageId, extra = {}) {
  return {
    prefix,
    suffix,
    languageId,
    model_thinking: settings.modelThinking,
    privacy_mode: settings.privacyMode,
    user_preferences: {
      language: settings.language,
      country: settings.country,
      timezone: settings.detectedTimezone,
    },
    ...extra,
  }
}

async function consumeSseJson({
  url,
  body,
  requestId,
  signal,
  timeoutMs,
  onChunk,
  onEvent,
  onDone,
}) {
  const requestController = new AbortController()
  const timeoutId = timeoutMs ? setTimeout(() => requestController.abort('timeout'), timeoutMs) : null
  const cancelUrl = getCancelUrl(url)

  const relayAbort = () => {
    requestController.abort(signal?.reason || 'abort')
  }

  const onAbort = () => {
    const reason = normalizeAbortReason(requestController.signal.reason)
    void sendCancelRequest(cancelUrl, requestId, reason)
  }

  requestController.signal.addEventListener('abort', onAbort, { once: true })
  if (signal) {
    if (signal.aborted) {
      relayAbort()
    } else {
      signal.addEventListener('abort', relayAbort, { once: true })
    }
  }

  try {
    const headers = {
      'Content-Type': 'application/json',
      'X-Request-Id': requestId,
    }
    if (API_KEY) {
      headers['X-API-Key'] = API_KEY
    }
    const res = await fetch(url, {
      method: 'POST',
      headers,
      body: JSON.stringify(body),
      credentials: 'include',
      signal: requestController.signal,
    })

    if (!res.ok) {
      const errorText = await res.text()
      throw new Error(`HTTP ${res.status}: ${errorText}`)
    }
    if (!res.body) {
      throw new Error('流式响应不可用')
    }

    const reader = res.body.getReader()
    const decoder = new TextDecoder()
    let buffer = ''
    let finalPayload = null

    while (true) {
      const { done, value } = await reader.read()
      if (done) break
      buffer += decoder.decode(value, { stream: true })

      let boundary = buffer.indexOf('\n\n')
      while (boundary >= 0) {
        const chunk = buffer.slice(0, boundary)
        buffer = buffer.slice(boundary + 2)
        const parsed = parseSseEvent(chunk)
        const data = parsed.data ? JSON.parse(parsed.data) : {}

        onEvent?.(parsed.event, data)

        if (parsed.event === 'result') {
          onChunk?.(data)
        } else if (parsed.event === 'done') {
          finalPayload = data.result || data
          return onDone ? onDone(finalPayload) : finalPayload
        } else if (parsed.event === 'error') {
          throw new Error(String(data.error || '请求失败'))
        } else if (parsed.event === 'cancelled') {
          throw createAbortError('请求已取消')
        }

        boundary = buffer.indexOf('\n\n')
      }
    }

    if (requestController.signal.aborted) {
      throw createAbortError('请求已中止')
    }
    return onDone ? onDone(finalPayload || {}) : finalPayload
  } finally {
    if (timeoutId) clearTimeout(timeoutId)
    requestController.signal.removeEventListener('abort', onAbort)
    if (signal) {
      signal.removeEventListener('abort', relayAbort)
    }
  }
}

export async function fetchSuggestion(prefix, suffix, languageId, signal, apiUrl = API_URL) {
  let normalizedLanguageId = 'markdown'
  if (typeof languageId === 'string' && languageId.trim()) {
    normalizedLanguageId = languageId.trim()
  } else if (languageId && typeof languageId === 'object' && 'aborted' in languageId) {
    signal = languageId
  }
  if (typeof signal === 'string') {
    apiUrl = signal
    signal = undefined
  }

  const settings = useSettingsStore()
  const requestId = generateRequestId()
  let finalContent = ''

  const result = await consumeSseJson({
    url: apiUrl,
    body: buildCompletionBody(settings, prefix, suffix, normalizedLanguageId),
    requestId,
    signal,
    onChunk(data) {
      if (typeof data.content === 'string') {
        finalContent = data.content
      } else if (typeof data.delta === 'string') {
        finalContent += data.delta
      }
    },
    onDone(data) {
      return typeof data.content === 'string' ? data.content : finalContent
    },
  })

  return result || ''
}

export async function fetchProSuggestionStream(payload, apiUrl = PRO_URL) {
  const {
    prefix = '',
    suffix = '',
    languageId = 'markdown',
    instruction = '',
    signal,
    timeoutMs = PRO_FRONTEND_TIMEOUT_MS,
    onChunk,
    onEvent,
  } = payload || {}

  const settings = useSettingsStore()
  const requestId = generateRequestId()
  let finalContent = ''

  return consumeSseJson({
    url: apiUrl,
    requestId,
    signal,
    timeoutMs,
    body: {
      prefix,
      suffix,
      languageId: String(languageId || 'markdown').trim() || 'markdown',
      instruction,
      pro_thinking: settings.proThinking || 'medium',
      privacy_mode: settings.privacyMode,
      user_preferences: {
        language: settings.language,
        country: settings.country,
        timezone: settings.detectedTimezone,
      },
    },
    onChunk(data) {
      const delta = String(data.delta || data.content || '')
      if (delta) {
        finalContent += delta
        onChunk?.(delta)
      }
    },
    onEvent(event, data) {
      if (event === 'progress' && data.phase === 'thinking') {
        onEvent?.('thinking', data)
        return
      }
      if (event === 'queued' || event === 'started' || event === 'resource') {
        onEvent?.(event, data)
      }
    },
    onDone(data) {
      return String(data.content || finalContent || '')
    },
  })
}

export async function fetchWebSearchStream(payload, apiUrl = WEB_SEARCH_URL) {
  const {
    prefix = '',
    suffix = '',
    languageId = 'markdown',
    signal,
    timeoutMs = WEB_SEARCH_FRONTEND_TIMEOUT_MS,
    onEvent,
    onDelta,
  } = payload || {}

  const settings = useSettingsStore()
  const requestId = generateRequestId()

  return consumeSseJson({
    url: apiUrl,
    requestId,
    signal,
    timeoutMs,
    body: {
      prefix,
      suffix,
      languageId: String(languageId || 'markdown').trim() || 'markdown',
      privacy_mode: settings.privacyMode,
      user_preferences: {
        language: settings.language,
        country: settings.country,
        timezone: settings.detectedTimezone,
      },
    },
    onEvent(event, data) {
      if (event === 'queued' || event === 'started' || event === 'resource') {
        onEvent?.(event, data)
        return
      }
      if (event === 'progress') {
        onEvent?.(String(data?.phase || ''), data)
        return
      }
      if (event === 'delta') {
        onDelta?.(data)
        return
      }
      if (event === 'error') {
        onEvent?.('error', data)
      }
    },
    onDone(data) {
      return {
        content: String(data?.content || ''),
        createdAt: String(data?.created_at || data?.createdAt || ''),
      }
    },
  })
}

export async function fetchTTS(text, instruct = '', apiUrl = TTS_URL) {
  const requestId = generateRequestId()
  return consumeSseJson({
    url: apiUrl,
    requestId,
    body: { text, instruct, speaker: 'Vivian', format: 'wav' },
    onDone(data) {
      return data
    },
  })
}

export async function fetchTTSStatus(apiUrl = TTS_STATUS_URL) {
  const res = await safeFetch(apiUrl, {
    headers: buildHeaders({ 'Content-Type': 'application/json' }),
  })
  return parseJsonResponse(res)
}

export async function fetchTTSConfig(apiUrl = TTS_CONFIG_URL) {
  const res = await safeFetch(apiUrl, {
    headers: buildHeaders({ 'Content-Type': 'application/json' }),
  })
  return parseJsonResponse(res)
}

export async function submitCompress(content, docType = 'txt', apiUrl = COMPRESS_SUBMIT_URL) {
  const res = await safeFetch(apiUrl, {
    method: 'POST',
    headers: buildHeaders({
      'Content-Type': 'application/json',
    }),
    body: JSON.stringify({ content, docType }),
  })
  return parseJsonResponse(res)
}

export function pollCompressStatus(taskId, onStateChange, apiUrl = COMPRESS_STATUS_URL) {
  let consecutiveErrors = 0

  const interval = setInterval(async () => {
    try {
      const res = await safeFetch(`${apiUrl}?task_id=${encodeURIComponent(taskId)}`, {
        headers: buildHeaders({ 'Content-Type': 'application/json' }),
      })
      if (!res.ok) {
        consecutiveErrors++
        if (consecutiveErrors >= 5) {
          clearInterval(interval)
          onStateChange('error', '', `请求失败 HTTP ${res.status}`)
        }
        return
      }

      consecutiveErrors = 0
      const data = await parseJsonResponse(res)
      onStateChange(data.status, data.content || '', data.message, data)

      if (['completed', 'error', 'cancelled'].includes(data.status)) {
        clearInterval(interval)
      }
    } catch (err) {
      consecutiveErrors++
      if (consecutiveErrors >= 5) {
        clearInterval(interval)
        onStateChange('error', '', `网络异常: ${err.message || err}`)
      }
    }
  }, 1000)

  return { stop: () => clearInterval(interval) }
}

export async function fetchJobLoad(apiUrl = JOB_LOAD_URL) {
  const res = await safeFetch(apiUrl, {
    headers: buildHeaders({ 'Content-Type': 'application/json' }),
  })
  if (!res.ok) {
    throw new Error(`Job Load HTTP ${res.status}`)
  }
  return parseJsonResponse(res)
}
