/**
 * Multi-provider setup prompt for SANDCODE.
 * Runs BEFORE any Ink/React rendering to avoid keybinding conflicts.
 */
import * as readline from 'node:readline'
import { getAPIProvider } from './model/providers.js'
import { getGlobalConfig, saveGlobalConfig } from './config.js'

interface ProviderChoice {
  key: string
  name: string
  provider: string
  defaultModel: string
  envKey: string
  envEnable: string
  configKey: string
  baseUrlEnv?: string
  needsBaseUrl?: boolean
  isLMStudio?: boolean
  url?: string
}

const PROVIDERS: ProviderChoice[] = [
  {
    key: '1',
    name: 'z.ai (GLM-5.1)',
    provider: 'zai',
    defaultModel: 'GLM-5.1',
    envKey: 'ZAI_API_KEY',
    envEnable: 'SANDCODE_USE_ZAI',
    configKey: 'zaiApiKey',
    url: 'https://z.ai',
  },
  {
    key: '2',
    name: 'OpenAI (GPT-4o, o1, o3)',
    provider: 'openai',
    defaultModel: 'gpt-4o',
    envKey: 'OPENAI_API_KEY',
    envEnable: 'SANDCODE_USE_OPENAI',
    configKey: 'openaiApiKey',
    url: 'https://platform.openai.com/api-keys',
  },
  {
    key: '3',
    name: 'Google Gemini (2.5 Pro)',
    provider: 'gemini',
    defaultModel: 'gemini-2.5-pro',
    envKey: 'GEMINI_API_KEY',
    envEnable: 'SANDCODE_USE_GEMINI',
    configKey: 'geminiApiKey',
    url: 'https://aistudio.google.com/apikey',
  },
  {
    key: '4',
    name: 'DeepSeek',
    provider: 'deepseek',
    defaultModel: 'deepseek-chat',
    envKey: 'DEEPSEEK_API_KEY',
    envEnable: 'SANDCODE_USE_DEEPSEEK',
    configKey: 'deepseekApiKey',
    url: 'https://platform.deepseek.com/api_keys',
  },
  {
    key: '5',
    name: 'Mistral',
    provider: 'mistral',
    defaultModel: 'mistral-large-latest',
    envKey: 'MISTRAL_API_KEY',
    envEnable: 'SANDCODE_USE_MISTRAL',
    configKey: 'mistralApiKey',
    url: 'https://console.mistral.ai/api-keys',
  },
  {
    key: '6',
    name: 'Anthropic (Claude)',
    provider: 'claude',
    defaultModel: 'claude-sonnet-4-6',
    envKey: 'ANTHROPIC_API_KEY',
    envEnable: '',
    configKey: '',
    url: 'https://console.anthropic.com/settings/keys',
  },
  {
    key: '7',
    name: 'OpenRouter (200+ models)',
    provider: 'openrouter',
    defaultModel: 'anthropic/claude-sonnet-4',
    envKey: 'OPENROUTER_API_KEY',
    envEnable: 'SANDCODE_USE_OPENROUTER',
    configKey: 'openrouterApiKey',
    url: 'https://openrouter.ai/keys',
  },
  {
    key: '8',
    name: 'LM Studio (local models)',
    provider: 'openai-compat',
    defaultModel: '',
    envKey: 'OPENAI_COMPAT_API_KEY',
    envEnable: 'SANDCODE_USE_OPENAI_COMPAT',
    configKey: 'openaiCompatApiKey',
    isLMStudio: true,
  },
  {
    key: '9',
    name: 'Custom OpenAI-compatible endpoint',
    provider: 'openai-compat',
    defaultModel: '',
    envKey: 'OPENAI_COMPAT_API_KEY',
    envEnable: 'SANDCODE_USE_OPENAI_COMPAT',
    configKey: 'openaiCompatApiKey',
    needsBaseUrl: true,
  },
]

/**
 * If no provider is configured yet, prompt the user to select one.
 * Returns true if setup was performed.
 */
export async function promptProviderSetupIfNeeded(): Promise<boolean> {
  // Dev mode: always show provider picker regardless of saved config
  const devReset = process.env.SANDCODE_DEV_RESET === '1' || process.env.SANDCODE_DEV_RESET === 'true'

  if (!devReset) {
    const config = getGlobalConfig()

    // Already completed SANDCODE provider setup — skip
    if (config.provider) {
      if (config.provider === 'claude') return false
      // Custom providers always have their key in the customProviders array
      if (config.customProviders?.some(p => p.id === config.provider && p.apiKey)) return false
      const keyMap: Record<string, string | undefined> = {
        zai: config.zaiApiKey || process.env.ZAI_API_KEY,
        openai: config.openaiApiKey || process.env.OPENAI_API_KEY,
        deepseek: config.deepseekApiKey || process.env.DEEPSEEK_API_KEY,
        mistral: config.mistralApiKey || process.env.MISTRAL_API_KEY,
        gemini: config.geminiApiKey || process.env.GEMINI_API_KEY,
        'openai-compat': config.openaiCompatApiKey || process.env.OPENAI_COMPAT_API_KEY,
        'openrouter': config.openrouterApiKey || process.env.OPENROUTER_API_KEY,
      }
      if (keyMap[config.provider]) return false
    }

    // No SANDCODE provider configured yet — but check if a SANDCODE-specific
    // env var is set (user explicitly configured a provider outside the setup flow)
    if (process.env.ZAI_API_KEY && process.env.SANDCODE_USE_ZAI) return false
    if (process.env.OPENAI_API_KEY && process.env.SANDCODE_USE_OPENAI) return false
    if (process.env.DEEPSEEK_API_KEY && process.env.SANDCODE_USE_DEEPSEEK) return false
    if (process.env.MISTRAL_API_KEY && process.env.SANDCODE_USE_MISTRAL) return false
    if (process.env.GEMINI_API_KEY && process.env.SANDCODE_USE_GEMINI) return false
    if (process.env.OPENROUTER_API_KEY && process.env.SANDCODE_USE_OPENROUTER) return false
  }

  // Don't prompt in non-interactive mode
  if (!process.stdin.isTTY) return false

  console.log('')
  console.log('      \x1b[33m▜▀▀▀▀▀▛\x1b[0m')
  console.log('      \x1b[33m ▜░░░▛ \x1b[0m')
  console.log('      \x1b[33m  ▐▓▌  \x1b[0m')
  console.log('      \x1b[33m ▟▓▓▓▙ \x1b[0m')
  console.log('      \x1b[33m▙▄▄▄▄▄▟\x1b[0m')
  console.log('')
  console.log('  \x1b[1m\x1b[33mSANDCODE\x1b[0m — Provider Setup')
  console.log('')
  console.log('  Select your AI provider:')
  console.log('')
  for (const p of PROVIDERS) {
    const suffix = p.key === '1' ? '  \x1b[2m— default\x1b[0m' : ''
    console.log(`  [\x1b[33m${p.key}\x1b[0m] ${p.name}${suffix}`)
  }
  console.log('')

  const choice = await askForInput('  Provider > ')
  const trimmedChoice = (choice || '1').trim()
  const selected = PROVIDERS.find(p => p.key === trimmedChoice)

  if (!selected) {
    console.log('  Invalid choice. Defaulting to z.ai.')
    return promptProviderSetupIfNeeded()
  }

  // Anthropic (Claude) — just set provider and let normal auth flow handle it
  if (selected.provider === 'claude') {
    saveGlobalConfig(current => ({
      ...current,
      provider: 'claude',
    }))
    console.log('')
    console.log('  \x1b[32m✓\x1b[0m Provider set to Anthropic. Set \x1b[1mANTHROPIC_API_KEY\x1b[0m env var or use OAuth.')
    console.log('')
    return true
  }

  // LM Studio — auto-detect server and loaded models
  if (selected.isLMStudio) {
    const portInput = await askForInput('  LM Studio port (default 1234) > ')
    const port = portInput?.trim() || '1234'
    const lmBaseUrl = `http://localhost:${port}/v1`

    console.log(`  Checking ${lmBaseUrl}...`)

    // Try to list loaded models
    let defaultModel = ''
    try {
      const res = await fetch(`${lmBaseUrl}/models`, { signal: AbortSignal.timeout(3000) })
      if (res.ok) {
        const data = (await res.json()) as { data?: Array<{ id: string }> }
        const models = data.data ?? []
        if (models.length > 0) {
          console.log(`  \x1b[32m✓\x1b[0m Connected! Loaded models:`)
          for (const m of models) {
            console.log(`    · ${m.id}`)
          }
          defaultModel = models[0]!.id
        } else {
          console.log('  \x1b[33m!\x1b[0m Connected, but no models loaded. Load a model in LM Studio first.')
        }
      } else {
        console.log(`  \x1b[33m!\x1b[0m Could not connect. Make sure LM Studio is running with the server enabled.`)
      }
    } catch {
      console.log(`  \x1b[33m!\x1b[0m Could not connect to localhost:${port}. Start LM Studio and enable the server.`)
    }

    if (!defaultModel) {
      const modelInput = await askForInput('  Model name (or press Enter to continue) > ')
      defaultModel = modelInput?.trim() || 'default'
    }

    // LM Studio doesn't need an API key — use placeholder
    const trimmedKey = 'lm-studio'
    const baseUrl = lmBaseUrl

    saveGlobalConfig(current => ({
      ...current,
      provider: 'openai-compat' as const,
      openaiCompatApiKey: trimmedKey,
      openaiCompatBaseUrl: baseUrl,
      openaiCompatDefaultModel: defaultModel,
      env: {
        ...(current.env || {}),
        OPENAI_COMPAT_API_KEY: trimmedKey,
        OPENAI_COMPAT_BASE_URL: baseUrl,
        SANDCODE_USE_OPENAI_COMPAT: '1',
        SANDCODE_DEFAULT_MODEL: defaultModel,
        API_TIMEOUT_MS: '3000000',
      },
    }))

    process.env.OPENAI_COMPAT_API_KEY = trimmedKey
    process.env.OPENAI_COMPAT_BASE_URL = baseUrl
    process.env.SANDCODE_USE_OPENAI_COMPAT = '1'
    process.env.SANDCODE_DEFAULT_MODEL = defaultModel

    console.log('')
    console.log(`  \x1b[32m✓\x1b[0m LM Studio configured (${defaultModel}). Starting \x1b[33mSANDCODE\x1b[0m...`)
    console.log('')
    return true
  }

  // Show URL if available
  if (selected.url) {
    console.log(`  Get an API key at: \x1b[4m${selected.url}\x1b[0m`)
    console.log('')
  }

  const apiKey = await askForInput('  API Key > ')
  if (!apiKey || apiKey.trim().length === 0) {
    console.log(`  No key entered. Set \x1b[1m${selected.envKey}\x1b[0m env var later.`)
    return false
  }

  const trimmedKey = apiKey.trim()

  // OpenRouter — ask for default model (200+ available)
  if (selected.provider === 'openrouter') {
    console.log('  OpenRouter supports 200+ models. Enter a default model:')
    console.log('  Examples: anthropic/claude-sonnet-4, openai/gpt-4o, google/gemini-2.5-pro')
    console.log('')
    const modelInput = await askForInput(`  Default model (${selected.defaultModel}) > `)
    const defaultModel = modelInput?.trim() || selected.defaultModel

    saveGlobalConfig(current => ({
      ...current,
      provider: 'openrouter' as const,
      openrouterApiKey: trimmedKey,
      env: {
        ...(current.env || {}),
        OPENROUTER_API_KEY: trimmedKey,
        SANDCODE_USE_OPENROUTER: '1',
        SANDCODE_DEFAULT_MODEL: defaultModel,
        OPENAI_COMPAT_API_KEY: trimmedKey,
        OPENAI_COMPAT_BASE_URL: 'https://openrouter.ai/api/v1',
        API_TIMEOUT_MS: '3000000',
      },
    }))

    process.env.OPENROUTER_API_KEY = trimmedKey
    process.env.SANDCODE_USE_OPENROUTER = '1'
    process.env.OPENAI_COMPAT_API_KEY = trimmedKey
    process.env.OPENAI_COMPAT_BASE_URL = 'https://openrouter.ai/api/v1'
    process.env.SANDCODE_DEFAULT_MODEL = defaultModel

    console.log('')
    console.log(`  \x1b[32m✓\x1b[0m OpenRouter configured (${defaultModel}). Starting \x1b[33mSANDCODE\x1b[0m...`)
    console.log('')
    return true
  }

  // For custom endpoints, also ask for base URL and model
  let baseUrl = ''
  let defaultModel = selected.defaultModel
  if (selected.needsBaseUrl) {
    const urlInput = await askForInput('  Base URL (e.g., http://localhost:11434/v1) > ')
    baseUrl = urlInput?.trim() || 'http://localhost:11434/v1'
    const modelInput = await askForInput('  Default model name > ')
    defaultModel = modelInput?.trim() || 'default'
  }

  // Save to config
  saveGlobalConfig(current => {
    const update: Record<string, unknown> = {
      ...current,
      provider: selected.provider,
      [selected.configKey]: trimmedKey,
      env: {
        ...(current.env || {}),
        [selected.envKey]: trimmedKey,
        ...(selected.envEnable ? { [selected.envEnable]: '1' } : {}),
        SANDCODE_DEFAULT_MODEL: defaultModel || selected.defaultModel,
        API_TIMEOUT_MS: '3000000',
      },
    }
    if (baseUrl) {
      update.openaiCompatBaseUrl = baseUrl
      ;(update.env as Record<string, string>).OPENAI_COMPAT_BASE_URL = baseUrl
    }
    if (selected.needsBaseUrl && defaultModel) {
      update.openaiCompatDefaultModel = defaultModel
    }
    return update as typeof current
  })

  // Set in current process
  process.env[selected.envKey] = trimmedKey
  if (selected.envEnable) {
    process.env[selected.envEnable] = '1'
  }

  console.log('')
  console.log(`  \x1b[32m✓\x1b[0m ${selected.name} configured. Starting \x1b[33mSANDCODE\x1b[0m...`)
  console.log('')

  return true
}

function askForInput(prompt: string): Promise<string> {
  return new Promise(resolve => {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
    })
    rl.question(prompt, answer => {
      rl.close()
      resolve(answer)
    })
  })
}

// ─────────────────────────────────────────────────────────────
// Multi-provider management (used by model picker at runtime)
// ─────────────────────────────────────────────────────────────

export interface ConfiguredProvider {
  id: string
  name: string
  isActive: boolean
  model?: string
}

/**
 * Returns all providers that have been configured (have an API key saved).
 */
export function getConfiguredProviders(): ConfiguredProvider[] {
  const config = getGlobalConfig()
  const active = getAPIProvider()
  const result: ConfiguredProvider[] = []

  const checks: Array<{ id: string; name: string; key: string | undefined; model?: string }> = [
    { id: 'zai', name: 'z.ai', key: config.zaiApiKey || process.env.ZAI_API_KEY, model: 'GLM-5.1' },
    { id: 'openai', name: 'OpenAI', key: config.openaiApiKey || process.env.OPENAI_API_KEY },
    { id: 'deepseek', name: 'DeepSeek', key: config.deepseekApiKey || process.env.DEEPSEEK_API_KEY },
    { id: 'mistral', name: 'Mistral', key: config.mistralApiKey || process.env.MISTRAL_API_KEY },
    { id: 'gemini', name: 'Google Gemini', key: config.geminiApiKey || process.env.GEMINI_API_KEY },
    { id: 'openrouter', name: 'OpenRouter', key: config.openrouterApiKey || process.env.OPENROUTER_API_KEY },
    {
      id: 'openai-compat',
      name: config.openaiCompatBaseUrl?.includes('localhost') ? 'LM Studio' : 'OpenAI-compat',
      key: config.openaiCompatApiKey || process.env.OPENAI_COMPAT_API_KEY,
      model: config.openaiCompatDefaultModel,
    },
  ]

  // Also check if Anthropic is configured
  if (process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN) {
    result.push({ id: 'firstParty', name: 'Anthropic', isActive: active === 'firstParty' })
  }

  for (const c of checks) {
    if (c.key) {
      result.push({
        id: c.id,
        name: c.name,
        isActive: active === c.id,
        model: c.model,
      })
    }
  }

  // Include user-added custom providers
  for (const cp of config.customProviders ?? []) {
    // Don't duplicate if already listed above
    if (!result.some(r => r.id === cp.id)) {
      result.push({
        id: cp.id,
        name: cp.name,
        isActive: config.provider === cp.id,
        model: cp.defaultModel,
      })
    }
  }

  return result
}

/**
 * Switch the active provider. Updates config and current process env.
 * Returns the new provider name.
 */
export function switchActiveProvider(providerId: string): string {
  const config = getGlobalConfig()

  // Map provider ID to config values and env vars
  const providerEnvMap: Record<string, { envEnable: string; envKey: string; configKey: string }> = {
    zai: { envEnable: 'SANDCODE_USE_ZAI', envKey: 'ZAI_API_KEY', configKey: 'zaiApiKey' },
    openai: { envEnable: 'SANDCODE_USE_OPENAI', envKey: 'OPENAI_API_KEY', configKey: 'openaiApiKey' },
    deepseek: { envEnable: 'SANDCODE_USE_DEEPSEEK', envKey: 'DEEPSEEK_API_KEY', configKey: 'deepseekApiKey' },
    mistral: { envEnable: 'SANDCODE_USE_MISTRAL', envKey: 'MISTRAL_API_KEY', configKey: 'mistralApiKey' },
    gemini: { envEnable: 'SANDCODE_USE_GEMINI', envKey: 'GEMINI_API_KEY', configKey: 'geminiApiKey' },
    openrouter: { envEnable: 'SANDCODE_USE_OPENROUTER', envKey: 'OPENROUTER_API_KEY', configKey: 'openrouterApiKey' },
    'openai-compat': { envEnable: 'SANDCODE_USE_OPENAI_COMPAT', envKey: 'OPENAI_COMPAT_API_KEY', configKey: 'openaiCompatApiKey' },
    firstParty: { envEnable: '', envKey: 'ANTHROPIC_API_KEY', configKey: '' },
  }

  // Clear all provider env enables first
  for (const m of Object.values(providerEnvMap)) {
    if (m.envEnable) {
      delete process.env[m.envEnable]
    }
  }

  const mapping = providerEnvMap[providerId]

  // Check if this is a custom provider
  const customProvider = config.customProviders?.find(p => p.id === providerId)
  if (customProvider) {
    // Custom providers route through openai-compat
    process.env.OPENAI_COMPAT_API_KEY = customProvider.apiKey
    process.env.OPENAI_COMPAT_BASE_URL = customProvider.baseUrl
    process.env.SANDCODE_USE_OPENAI_COMPAT = '1'
    process.env.SANDCODE_DEFAULT_MODEL = customProvider.defaultModel

    saveGlobalConfig(current => ({
      ...current,
      provider: providerId,
    }))
    return providerId
  }

  if (!mapping) return providerId

  // Set the new provider
  if (mapping.envEnable) {
    process.env[mapping.envEnable] = '1'
  }

  // Set API key from config if not already in env
  const apiKey = (config as Record<string, unknown>)[mapping.configKey] as string | undefined
  if (apiKey && !process.env[mapping.envKey]) {
    process.env[mapping.envKey] = apiKey
  }

  // For openai-compat, also set base URL
  if (providerId === 'openai-compat' && config.openaiCompatBaseUrl) {
    process.env.OPENAI_COMPAT_BASE_URL = config.openaiCompatBaseUrl
  }

  // For openrouter, set the API key in env if available
  if (providerId === 'openrouter') {
    const orKey = config.openrouterApiKey || process.env.OPENROUTER_API_KEY
    if (orKey) process.env.OPENROUTER_API_KEY = orKey
  }

  // Save active provider to config
  const configProvider = providerId === 'firstParty' ? 'claude' : providerId
  saveGlobalConfig(current => ({
    ...current,
    provider: configProvider as typeof current.provider,
  }))

  return providerId
}

/**
 * Add a new custom provider to the config.
 * Returns the provider ID.
 */
export function addCustomProvider(provider: {
  id: string
  name: string
  baseUrl: string
  apiKey: string
  defaultModel: string
  models?: Array<{ value: string; label: string; desc: string }>
  extraHeaders?: Record<string, string>
}): string {
  saveGlobalConfig(current => {
    const existing = current.customProviders ?? []
    // Replace if same ID already exists
    const filtered = existing.filter(p => p.id !== provider.id)
    return {
      ...current,
      customProviders: [...filtered, provider],
    }
  })
  return provider.id
}

/**
 * Remove a custom provider from the config.
 */
export function removeCustomProvider(providerId: string): boolean {
  const config = getGlobalConfig()
  const existing = config.customProviders ?? []
  if (!existing.some(p => p.id === providerId)) return false

  saveGlobalConfig(current => ({
    ...current,
    customProviders: (current.customProviders ?? []).filter(p => p.id !== providerId),
    // If this was the active provider, clear it
    ...(current.provider === providerId ? { provider: undefined } : {}),
  }))
  return true
}

/**
 * Interactive prompt to add a new custom provider.
 * Called from /provider add or during onboarding.
 */
export async function promptAddCustomProvider(): Promise<string | null> {
  if (!process.stdin.isTTY) return null

  console.log('')
  console.log('  \x1b[1m\x1b[33mAdd Custom Provider\x1b[0m')
  console.log('')

  const name = await askForInput('  Provider name (e.g., Together.ai) > ')
  if (!name?.trim()) return null

  const baseUrl = await askForInput('  Base URL (e.g., https://api.together.xyz/v1) > ')
  if (!baseUrl?.trim()) return null

  const apiKey = await askForInput('  API Key > ')
  if (!apiKey?.trim()) return null

  const defaultModel = await askForInput('  Default model name > ')
  if (!defaultModel?.trim()) return null

  // Generate a slug ID from the name
  const id = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')

  addCustomProvider({
    id,
    name: name.trim(),
    baseUrl: baseUrl.trim(),
    apiKey: apiKey.trim(),
    defaultModel: defaultModel.trim(),
  })

  console.log('')
  console.log(`  \x1b[32m✓\x1b[0m ${name.trim()} added. Use /model to switch to it.`)
  console.log('')

  return id
}

/** Export PROVIDERS list for use by model picker */
export { PROVIDERS as PROVIDER_CHOICES }
