/**
 * RiOS Cloud: model IDs shown in /model should match what the user can call.
 * - GET {ANTHROPIC_BASE_URL}/v1/models (gateway allowlist)
 * - If OAuth session exists: GET {rios-api}/api/llm-keys/billing and intersect
 *   with the row whose fullApiKey matches the current inference key (console whitelist).
 */

import { getOauthConfig } from '../constants/oauth.js'
import { getAnthropicApiKey, getClaudeAIOAuthTokens } from '../utils/auth.js'
import { isRiOsGatewayAnthropicBaseUrl } from '../utils/model/providers.js'

const TTL_MS = 60_000
const FETCH_TIMEOUT_MS = 10_000

let cache: { ids: string[]; fetchedAt: number } | null = null

export function invalidateRiosCloudModelCatalogCache(): void {
  cache = null
}

function timeoutSignal(): AbortSignal {
  return AbortSignal.timeout(FETCH_TIMEOUT_MS)
}

async function fetchGatewayModelIds(baseUrl: string, apiKey: string): Promise<string[] | null> {
  try {
    const r = await fetch(`${baseUrl}/v1/models`, {
      headers: {
        Authorization: `Bearer ${apiKey}`,
        Accept: 'application/json',
      },
      signal: timeoutSignal(),
    })
    if (!r.ok) {
      return null
    }
    const j = (await r.json()) as { data?: { id?: string }[] }
    const data = j?.data
    if (!Array.isArray(data)) {
      return null
    }
    const ids = data.map(e => e?.id).filter((id): id is string => Boolean(id && typeof id === 'string'))
    return [...new Set(ids)]
  } catch {
    return null
  }
}

async function fetchBillingAllowedModelsForKey(fullApiKey: string): Promise<string[] | null> {
  const tokens = getClaudeAIOAuthTokens()
  if (!tokens?.accessToken) {
    return null
  }
  const apiRoot = getOauthConfig().BASE_API_URL.replace(/\/$/, '')
  try {
    const r = await fetch(`${apiRoot}/api/llm-keys/billing`, {
      headers: {
        Authorization: `Bearer ${tokens.accessToken}`,
        Accept: 'application/json',
      },
      signal: timeoutSignal(),
    })
    if (!r.ok) {
      return null
    }
    const j = (await r.json()) as {
      items?: { fullApiKey?: string; allowedModels?: string[] }[]
    }
    const items = j?.items
    if (!Array.isArray(items)) {
      return null
    }
    const row = items.find(i => i.fullApiKey === fullApiKey)
    if (!row?.allowedModels || !Array.isArray(row.allowedModels) || row.allowedModels.length === 0) {
      return null
    }
    return row.allowedModels.filter((m): m is string => typeof m === 'string' && m.length > 0)
  } catch {
    return null
  }
}

/**
 * Returns sorted unique model IDs for the RiOS /model picker, or null if unavailable
 * (not RiOS gateway, missing key, or fetch failure — caller falls back to built-in list).
 */
export async function fetchRiosCloudModelCatalog(force = false): Promise<string[] | null> {
  if (!isRiOsGatewayAnthropicBaseUrl()) {
    return null
  }
  if (!force && cache && Date.now() - cache.fetchedAt < TTL_MS) {
    return cache.ids
  }

  const baseUrl = process.env.ANTHROPIC_BASE_URL?.replace(/\/$/, '')
  const apiKey = getAnthropicApiKey()
  if (!baseUrl || !apiKey) {
    return null
  }

  const gatewayIds = await fetchGatewayModelIds(baseUrl, apiKey)
  if (!gatewayIds?.length) {
    return null
  }

  const billingIds = await fetchBillingAllowedModelsForKey(apiKey)
  let merged: string[]
  if (billingIds?.length) {
    const g = new Set(gatewayIds.map(s => s.toLowerCase()))
    merged = billingIds.filter(id => g.has(id.toLowerCase()))
    if (merged.length === 0) {
      merged = billingIds.slice()
    }
  } else {
    merged = gatewayIds
  }

  const ids = [...new Set(merged)].sort((a, b) => a.localeCompare(b))
  cache = { ids, fetchedAt: Date.now() }
  return ids
}
