import type { ProviderAdapter, ProviderCustomModel } from './types'

const GEMINI_DEFAULT_BASE_URL = 'https://generativelanguage.googleapis.com'
const ANTHROPIC_API_VERSION = '2023-06-01'
const FETCH_TIMEOUT_MS = 15_000

async function fetchJson(url: string, init: RequestInit): Promise<unknown> {
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS)
  try {
    const res = await fetch(url, { ...init, signal: controller.signal })
    if (!res.ok) {
      const body = await res.text().catch(() => '')
      throw new Error(`HTTP ${res.status}: ${body.slice(0, 200)}`)
    }
    return await res.json()
  }
  finally {
    clearTimeout(timer)
  }
}

function stripTrailingSlash(url: string): string {
  return url.replace(/\/+$/, '')
}

async function fetchOpenAiModels(
  apiKey: string,
  baseUrl?: string,
): Promise<ProviderCustomModel[]> {
  const base = stripTrailingSlash(baseUrl || 'https://api.openai.com/v1')
  const url = `${base}/models`
  const data = (await fetchJson(url, {
    method: 'GET',
    headers: { Authorization: `Bearer ${apiKey}` },
  })) as { data?: Array<{ id?: string }> }
  const models = Array.isArray(data?.data) ? data.data : []
  return models
    .map(m => ({ id: String(m?.id ?? '').trim() }))
    .filter(m => m.id)
}

async function fetchGeminiModels(
  apiKey: string,
  baseUrl?: string,
): Promise<ProviderCustomModel[]> {
  const base = stripTrailingSlash(baseUrl || GEMINI_DEFAULT_BASE_URL)
  const results: ProviderCustomModel[] = []
  let pageToken: string | undefined

  do {
    const params = new URLSearchParams({ key: apiKey })
    if (pageToken)
      params.set('pageToken', pageToken)
    const url = `${base}/v1beta/models?${params}`
    const data = (await fetchJson(url, { method: 'GET' })) as {
      models?: Array<{
        name?: string
        displayName?: string
        supportedGenerationMethods?: string[]
      }>
      nextPageToken?: string
    }
    const models = Array.isArray(data?.models) ? data.models : []
    for (const model of models) {
      const methods = Array.isArray(model?.supportedGenerationMethods)
        ? model.supportedGenerationMethods.map(v =>
            String(v ?? '').toLowerCase(),
          )
        : []
      if (methods.length && !methods.includes('generatecontent'))
        continue
      const rawName = String(model?.name ?? '').trim()
      if (!rawName)
        continue
      const id = rawName.startsWith('models/')
        ? rawName.slice('models/'.length)
        : rawName
      if (!id)
        continue
      const displayName = String(model?.displayName ?? '').trim()
      results.push({ id, name: displayName || undefined })
    }
    pageToken = data?.nextPageToken ? String(data.nextPageToken) : undefined
  } while (pageToken)

  return results
}

async function fetchAnthropicModels(
  apiKey: string,
  baseUrl?: string,
): Promise<ProviderCustomModel[]> {
  const base = stripTrailingSlash(baseUrl || 'https://api.anthropic.com')
  const url = `${base}/v1/models`
  const data = (await fetchJson(url, {
    method: 'GET',
    headers: {
      'x-api-key': apiKey,
      'anthropic-version': ANTHROPIC_API_VERSION,
    },
  })) as { data?: Array<{ id?: string, display_name?: string }> }
  const items = Array.isArray(data?.data) ? data.data : []
  return items
    .map(m => ({
      id: String(m?.id ?? '').trim(),
      name: String(m?.display_name ?? '').trim() || undefined,
    }))
    .filter(m => m.id)
}

export async function fetchModelsForProviderAdapter(
  input: Readonly<{
    adapter: ProviderAdapter
    baseUrl: string
    apiKey: string
  }>,
): Promise<ProviderCustomModel[]> {
  const apiKey = String(input.apiKey ?? '').trim()
  if (!apiKey)
    throw new Error('API key is required')

  const adapter = input.adapter
  const baseUrl = String(input.baseUrl ?? '').trim() || undefined

  if (adapter === 'openai' || adapter === 'openai-responses')
    return fetchOpenAiModels(apiKey, baseUrl)
  if (adapter === 'gemini')
    return fetchGeminiModels(apiKey, baseUrl)
  if (adapter === 'anthropic')
    return fetchAnthropicModels(apiKey, baseUrl)

  throw new Error(`Unsupported adapter: ${adapter}`)
}

export async function testProviderConnection(
  input: Readonly<{
    adapter: ProviderAdapter
    baseUrl: string
    apiKey: string
  }>,
): Promise<void> {
  const adapter = input.adapter
  const apiKey = String(input.apiKey ?? '').trim()
  const baseUrl = String(input.baseUrl ?? '').trim() || undefined

  if (adapter === 'openai' || adapter === 'openai-responses') {
    await fetchOpenAiModels(apiKey, baseUrl)
    return
  }

  if (adapter === 'gemini') {
    const base = stripTrailingSlash(baseUrl || GEMINI_DEFAULT_BASE_URL)
    const params = new URLSearchParams({ key: apiKey, pageSize: '1' })
    const url = `${base}/v1beta/models?${params}`
    await fetchJson(url, { method: 'GET' })
    return
  }

  if (adapter === 'anthropic') {
    await fetchAnthropicModels(apiKey, baseUrl)
  }
}
