import type { BedrockProviderOptions } from '@ai-sdk/amazon-bedrock'
import { type AnthropicProviderOptions } from '@ai-sdk/anthropic'
import type { GoogleGenerativeAIProviderOptions } from '@ai-sdk/google'
import type { OpenAIResponsesProviderOptions } from '@ai-sdk/openai'
import type { XaiProviderOptions } from '@ai-sdk/xai'
import { loggerService } from '@logger'
import {
  getModelSupportedVerbosity,
  isAnthropicModel,
  isGeminiModel,
  isGrokModel,
  isOpenAIModel,
  isQwenMTModel,
  isReasoningModel,
  isSupportFlexServiceTierModel,
  isSupportVerbosityModel
} from '@renderer/config/models'
import { mapLanguageToQwenMTModel } from '@renderer/config/translate'
import { getStoreSetting } from '@renderer/hooks/useSettings'
import { getProviderById } from '@renderer/services/ProviderService'
import {
  type Assistant,
  type GroqServiceTier,
  GroqServiceTiers,
  type GroqSystemProvider,
  isGroqServiceTier,
  isGroqSystemProvider,
  isOpenAIServiceTier,
  isTranslateAssistant,
  type Model,
  type NotGroqProvider,
  type OpenAIServiceTier,
  OpenAIServiceTiers,
  type Provider,
  type ServiceTier,
  SystemProviderIds
} from '@renderer/types'
import { type AiSdkParam, isAiSdkParam, type OpenAIVerbosity } from '@renderer/types/aiCoreTypes'
import { isSupportServiceTierProvider, isSupportVerbosityProvider } from '@renderer/utils/provider'
import type { JSONValue } from 'ai'
import { t } from 'i18next'
import { merge } from 'lodash'
import type { OllamaProviderOptions } from 'ollama-ai-provider-v2'

import { addAnthropicHeaders } from '../prepareParams/header'
import { getAiSdkProviderId } from '../provider/factory'
import type { ProviderCapabilities } from '../types'
import { buildGeminiGenerateImageParams } from './image'
import {
  getAnthropicReasoningParams,
  getBedrockReasoningParams,
  getCustomParameters,
  getGeminiReasoningParams,
  getOllamaReasoningParams,
  getOpenAIReasoningParams,
  getReasoningEffort,
  getXAIReasoningParams
} from './reasoning'
import { getWebSearchParams } from './websearch'

const logger = loggerService.withContext('aiCore.utils.options')

function toOpenAIServiceTier(model: Model, serviceTier: ServiceTier): OpenAIServiceTier {
  if (
    !isOpenAIServiceTier(serviceTier) ||
    (serviceTier === OpenAIServiceTiers.flex && !isSupportFlexServiceTierModel(model))
  ) {
    return undefined
  } else {
    return serviceTier
  }
}

function toGroqServiceTier(model: Model, serviceTier: ServiceTier): GroqServiceTier {
  if (
    !isGroqServiceTier(serviceTier) ||
    (serviceTier === GroqServiceTiers.flex && !isSupportFlexServiceTierModel(model))
  ) {
    return undefined
  } else {
    return serviceTier
  }
}

function getServiceTier<T extends GroqSystemProvider>(model: Model, provider: T): GroqServiceTier
function getServiceTier<T extends NotGroqProvider>(model: Model, provider: T): OpenAIServiceTier
function getServiceTier<T extends Provider>(model: Model, provider: T): OpenAIServiceTier | GroqServiceTier {
  const serviceTierSetting = provider.serviceTier

  if (!isSupportServiceTierProvider(provider) || !isOpenAIModel(model) || !serviceTierSetting) {
    return undefined
  }

  // 处理不同供应商需要 fallback 到默认值的情况
  if (isGroqSystemProvider(provider)) {
    return toGroqServiceTier(model, serviceTierSetting)
  } else {
    // 其他 OpenAI 供应商，假设他们的服务层级设置和 OpenAI 完全相同
    return toOpenAIServiceTier(model, serviceTierSetting)
  }
}

function getVerbosity(model: Model): OpenAIVerbosity {
  if (!isSupportVerbosityModel(model) || !isSupportVerbosityProvider(getProviderById(model.provider)!)) {
    return undefined
  }
  const openAI = getStoreSetting('openAI')

  const userVerbosity = openAI.verbosity

  if (userVerbosity) {
    const supportedVerbosity = getModelSupportedVerbosity(model)
    // Use user's verbosity if supported, otherwise use the first supported option
    const verbosity = supportedVerbosity.includes(userVerbosity) ? userVerbosity : supportedVerbosity[0]
    return verbosity
  }
  return undefined
}

/**
 * Extract AI SDK standard parameters from custom parameters
 * These parameters should be passed directly to streamText() instead of providerOptions
 */
export function extractAiSdkStandardParams(customParams: Record<string, any>): {
  standardParams: Partial<Record<AiSdkParam, any>>
  providerParams: Record<string, any>
} {
  const standardParams: Partial<Record<AiSdkParam, any>> = {}
  const providerParams: Record<string, any> = {}

  for (const [key, value] of Object.entries(customParams)) {
    if (isAiSdkParam(key)) {
      standardParams[key] = value
    } else {
      providerParams[key] = value
    }
  }

  return { standardParams, providerParams }
}

/**
 * 构建 AI SDK 的 providerOptions
 * 按 provider 类型分离，保持类型安全
 * 返回格式：{
 *   providerOptions: { 'providerId': providerOptions },
 *   standardParams: { topK, frequencyPenalty, presencePenalty, stopSequences, seed }
 * }
 *
 * Custom parameters are split into two categories:
 * 1. AI SDK standard parameters (topK, frequencyPenalty, etc.) - returned separately to be passed to streamText()
 * 2. Provider-specific parameters - merged into providerOptions
 */
export function buildProviderOptions(
  assistant: Assistant,
  model: Model,
  actualProvider: Provider,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>
): {
  providerOptions: Record<string, Record<string, JSONValue>>
  standardParams: Partial<Record<AiSdkParam, any>>
} {
  const rawProviderId = getAiSdkProviderId(actualProvider)
  logger.debug('buildProviderOptions', { assistant, model, actualProvider, capabilities, rawProviderId })
  // 构建 provider 特定的选项
  let providerSpecificOptions: Record<string, any> = {}
  const serviceTier = getServiceTier(model, actualProvider)
  const textVerbosity = getVerbosity(model)

  // 根据 provider ID 构建特定选项
  switch (rawProviderId) {
    case 'openai':
    case 'openai-chat':
    case 'azure':
    case 'azure-responses':
    case 'huggingface':
      providerSpecificOptions = buildOpenAIProviderOptions(assistant, model, capabilities, serviceTier, textVerbosity)
      break
    case 'anthropic':
    case 'azure-anthropic':
    case 'google-vertex-anthropic':
      providerSpecificOptions = buildAnthropicProviderOptions(assistant, model, capabilities)
      break
    case 'google':
    case 'google-vertex':
      providerSpecificOptions = buildGeminiProviderOptions(assistant, model, capabilities)
      break
    case 'xai':
    case 'xai-responses':
      providerSpecificOptions = buildXAIProviderOptions(assistant, model, capabilities)
      break
    case 'bedrock':
      providerSpecificOptions = buildBedrockProviderOptions(assistant, model, capabilities)
      break
    case SystemProviderIds.ollama:
      providerSpecificOptions = buildOllamaProviderOptions(assistant, model, capabilities)
      break
    case 'cherryin':
    case 'newapi':
    case 'aihubmix':
    case SystemProviderIds.gateway:
      providerSpecificOptions = buildAIGatewayOptions(assistant, model, capabilities, serviceTier, textVerbosity)
      break
    case 'deepseek':
    case 'openrouter':
    case 'openai-compatible':
    default:
      // 对于其他 provider，使用通用的构建逻辑
      providerSpecificOptions = buildGenericProviderOptions(rawProviderId, assistant, model, capabilities)
      // Merge serviceTier and textVerbosity
      providerSpecificOptions = {
        ...providerSpecificOptions,
        [rawProviderId]: {
          ...providerSpecificOptions[rawProviderId],
          serviceTier,
          textVerbosity
        }
      }
      break
  }
  logger.debug('Built providerSpecificOptions', { providerSpecificOptions })
  /**
   * Retrieve custom parameters and separate standard parameters from provider-specific parameters.
   */
  const customParams = getCustomParameters(assistant)
  const { standardParams, providerParams } = extractAiSdkStandardParams(customParams)
  logger.debug('Extracted standardParams and providerParams', { standardParams, providerParams })

  /**
   * Get the actual AI SDK provider ID(s) from the already-built providerSpecificOptions.
   * For proxy providers (cherryin, aihubmix, newapi), this will be the actual SDK provider (e.g., 'google', 'openai', 'anthropic')
   * For regular providers, this will be the provider itself
   */
  const actualAiSdkProviderIds = Object.keys(providerSpecificOptions)
  const primaryAiSdkProviderId = actualAiSdkProviderIds[0] // Use the first one as primary for non-scoped params

  // For openai-compatible providers, auto-convert reasoning_effort (snake_case) to reasoningEffort (camelCase).
  // The AI SDK's openai-compatible provider overwrites reasoning_effort to undefined,
  // but accepts reasoningEffort. See: https://github.com/CherryHQ/cherry-studio/issues/11987
  if (primaryAiSdkProviderId === 'openai-compatible' && 'reasoning_effort' in providerParams) {
    if (!('reasoningEffort' in providerParams)) {
      providerParams.reasoningEffort = providerParams.reasoning_effort
    }
    delete providerParams.reasoning_effort
  }

  /**
   * Merge custom parameters into providerSpecificOptions.
   * Simple logic:
   * 1. If key is in actualAiSdkProviderIds → merge directly (user knows the actual AI SDK provider ID)
   * 2. If key == rawProviderId:
   *    - If it's gateway/ollama → preserve (they need their own config for routing/options)
   *    - Otherwise → map to primary (this is a proxy provider like cherryin)
   * 3. Otherwise → treat as regular parameter, merge to primary provider
   *
   * Example:
   * - User writes `cherryin: { opt: 'val' }` → mapped to `google: { opt: 'val' }` (case 2, proxy)
   * - User writes `gateway: { order: [...] }` → stays as `gateway: { order: [...] }` (case 2, routing config)
   * - User writes `google: { opt: 'val' }` → stays as `google: { opt: 'val' }` (case 1)
   * - User writes `customKey: 'val'` → merged to `google: { customKey: 'val' }` (case 3)
   */
  for (const key of Object.keys(providerParams)) {
    if (actualAiSdkProviderIds.includes(key)) {
      // Case 1: Key is an actual AI SDK provider ID - merge directly
      providerSpecificOptions = {
        ...providerSpecificOptions,
        [key]: {
          ...providerSpecificOptions[key],
          ...providerParams[key]
        }
      }
    } else if (key === rawProviderId && !actualAiSdkProviderIds.includes(rawProviderId)) {
      // Case 2: Key is the current provider (not in actualAiSdkProviderIds, so it's a proxy or special provider)
      // Gateway is special: it needs routing config preserved
      if (key === SystemProviderIds.gateway) {
        // Preserve gateway config for routing
        providerSpecificOptions = {
          ...providerSpecificOptions,
          [key]: {
            ...providerSpecificOptions[key],
            ...providerParams[key]
          }
        }
      } else {
        // Proxy provider (cherryin, etc.) - map to actual AI SDK provider
        providerSpecificOptions = {
          ...providerSpecificOptions,
          [primaryAiSdkProviderId]: {
            ...providerSpecificOptions[primaryAiSdkProviderId],
            ...providerParams[key]
          }
        }
      }
    } else {
      // Case 3: Regular parameter - merge to primary provider
      providerSpecificOptions = {
        ...providerSpecificOptions,
        [primaryAiSdkProviderId]: {
          ...providerSpecificOptions[primaryAiSdkProviderId],
          [key]: providerParams[key]
        }
      }
    }
  }
  logger.debug('Final providerSpecificOptions after merging providerParams', { providerSpecificOptions })

  // 返回 AI Core SDK 要求的格式：{ 'providerId': providerOptions } 以及提取的标准参数
  return {
    providerOptions: providerSpecificOptions,
    standardParams
  }
}

/**
 * 构建 OpenAI 特定的 providerOptions
 */
function buildOpenAIProviderOptions(
  assistant: Assistant,
  model: Model,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>,
  serviceTier: OpenAIServiceTier,
  textVerbosity?: OpenAIVerbosity
): Record<string, OpenAIResponsesProviderOptions> {
  const { enableReasoning } = capabilities
  let providerOptions: OpenAIResponsesProviderOptions = {}
  // OpenAI 推理参数
  if (enableReasoning) {
    const reasoningParams = getOpenAIReasoningParams(assistant, model)
    providerOptions = {
      ...providerOptions,
      ...reasoningParams,
      // TODO: Remove this workaround after migrating to @ai-sdk/open-responses (#13462)
      // Bypass @ai-sdk/openai's model ID allowlist for reasoning detection.
      // Third-party providers often use non-canonical model IDs (e.g., "openai/gpt-5.2")
      // that fail the SDK's startsWith() checks, causing reasoning params to be silently dropped.
      ...(isReasoningModel(model) && { forceReasoning: true })
    }
  }
  const provider = getProviderById(model.provider)

  if (!provider) {
    throw new Error(`Provider ${model.provider} not found`)
  }

  if (isSupportVerbosityModel(model) && isSupportVerbosityProvider(provider)) {
    const openAI = getStoreSetting<'openAI'>('openAI')
    const userVerbosity = openAI?.verbosity

    if (userVerbosity && ['low', 'medium', 'high'].includes(userVerbosity)) {
      const supportedVerbosity = getModelSupportedVerbosity(model)
      // Use user's verbosity if supported, otherwise use the first supported option
      const verbosity = supportedVerbosity.includes(userVerbosity) ? userVerbosity : supportedVerbosity[0]

      providerOptions = {
        ...providerOptions,
        textVerbosity: verbosity
      }
    }
  }

  // TODO: 支持配置是否在服务端持久化
  providerOptions = {
    ...providerOptions,
    serviceTier,
    textVerbosity,
    store: false
  }

  return {
    openai: providerOptions
  }
}

/**
 * 构建 Anthropic 特定的 providerOptions
 */
function buildAnthropicProviderOptions(
  assistant: Assistant,
  model: Model,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>
): Record<string, AnthropicProviderOptions> {
  const { enableReasoning } = capabilities
  let providerOptions: AnthropicProviderOptions = {}

  // Anthropic 推理参数
  if (enableReasoning) {
    const reasoningParams = getAnthropicReasoningParams(assistant, model)
    providerOptions = {
      ...providerOptions,
      ...reasoningParams
    }
  }

  return {
    anthropic: {
      ...providerOptions
    }
  }
}

/**
 * 构建 Gemini 特定的 providerOptions
 */
function buildGeminiProviderOptions(
  assistant: Assistant,
  model: Model,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>
): Record<string, GoogleGenerativeAIProviderOptions> {
  const { enableReasoning, enableGenerateImage } = capabilities
  let providerOptions: GoogleGenerativeAIProviderOptions = {}

  // Gemini 推理参数
  if (enableReasoning) {
    const reasoningParams = getGeminiReasoningParams(assistant, model)
    providerOptions = {
      ...providerOptions,
      ...reasoningParams
    }
  }

  if (enableGenerateImage) {
    providerOptions = {
      ...providerOptions,
      ...buildGeminiGenerateImageParams()
    }
  }

  return {
    google: {
      ...providerOptions
    }
  }
}

function buildXAIProviderOptions(
  assistant: Assistant,
  model: Model,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>
): Record<string, XaiProviderOptions> {
  const { enableReasoning } = capabilities
  let providerOptions: Record<string, any> = {}

  if (enableReasoning) {
    const reasoningParams = getXAIReasoningParams(assistant, model)
    providerOptions = {
      ...providerOptions,
      ...reasoningParams
    }
  }

  return {
    xai: {
      ...providerOptions
    }
  }
}

/**
 * Build Bedrock providerOptions
 */
function buildBedrockProviderOptions(
  assistant: Assistant,
  model: Model,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>
): Record<string, BedrockProviderOptions> {
  const { enableReasoning } = capabilities
  let providerOptions: BedrockProviderOptions = {}

  if (enableReasoning) {
    const reasoningParams = getBedrockReasoningParams(assistant, model)
    providerOptions = {
      ...providerOptions,
      ...reasoningParams
    }
  }

  const betaHeaders = addAnthropicHeaders(assistant, model)
  if (betaHeaders.length > 0) {
    providerOptions.anthropicBeta = betaHeaders
  }

  return {
    bedrock: providerOptions
  }
}

function buildOllamaProviderOptions(
  assistant: Assistant,
  model: Model,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>
): Record<string, OllamaProviderOptions> {
  const { enableReasoning } = capabilities
  let options = {}

  if (enableReasoning) {
    options = {
      ...options,
      ...getOllamaReasoningParams(assistant, model)
    }
  }

  return { ollama: options }
}

/**
 * 构建通用的 providerOptions（用于其他 provider）
 */
function buildGenericProviderOptions(
  providerId: string,
  assistant: Assistant,
  model: Model,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>
): Record<string, any> {
  const { enableWebSearch } = capabilities
  let providerOptions: Record<string, any> = {}

  const reasoningParams = getReasoningEffort(assistant, model)
  logger.debug('reasoningParams', reasoningParams)
  providerOptions = {
    ...providerOptions,
    ...reasoningParams
  }

  if (enableWebSearch) {
    const webSearchParams = getWebSearchParams(model)
    providerOptions = merge({}, providerOptions, webSearchParams)
  }

  // 特殊处理 Qwen MT
  if (isQwenMTModel(model)) {
    if (isTranslateAssistant(assistant)) {
      const targetLanguage = assistant.targetLanguage
      const translationOptions = {
        source_lang: 'auto',
        target_lang: mapLanguageToQwenMTModel(targetLanguage)
      } as const
      if (!translationOptions.target_lang) {
        throw new Error(t('translate.error.not_supported', { language: targetLanguage.value }))
      }
      providerOptions.translation_options = translationOptions
    } else {
      throw new Error(t('translate.error.chat_qwen_mt'))
    }
  }

  return {
    [providerId]: providerOptions
  }
}

function buildAIGatewayOptions(
  assistant: Assistant,
  model: Model,
  capabilities: Pick<ProviderCapabilities, 'enableReasoning' | 'enableWebSearch' | 'enableGenerateImage'>,
  serviceTier: OpenAIServiceTier,
  textVerbosity?: OpenAIVerbosity
): Record<
  string,
  | OpenAIResponsesProviderOptions
  | AnthropicProviderOptions
  | GoogleGenerativeAIProviderOptions
  | Record<string, unknown>
> {
  // Proxy providers (CherryIN, NewAPI) may annotate model.endpoint_type to force a specific protocol.
  // Keys here must stay aligned with the language-model class each SDK layer builds, otherwise
  // AI SDK drops the custom provider options. See:
  //   - packages/ai-sdk-provider/src/cherryin-provider.ts (createChatModel)
  //   - src/renderer/src/aiCore/provider/custom/newapi-provider.ts (createChatModel)
  //
  //   endpoint_type      | SDK language-model class           | AI SDK providerOptions key
  //   -------------------+------------------------------------+---------------------------
  //   'anthropic'        | AnthropicMessagesLanguageModel     | anthropic
  //   'gemini'           | GoogleGenerativeAILanguageModel    | google
  //   'openai-response'  | OpenAIResponsesLanguageModel       | openai
  //   'openai'           | OpenAICompatibleChatLanguageModel  | openai-compatible
  //   'image-generation' | OpenAICompatibleChatLanguageModel  | openai-compatible
  switch (model.endpoint_type) {
    case 'anthropic':
      return buildAnthropicProviderOptions(assistant, model, capabilities)
    case 'gemini':
      return buildGeminiProviderOptions(assistant, model, capabilities)
    case 'openai-response':
      return buildOpenAIProviderOptions(assistant, model, capabilities, serviceTier, textVerbosity)
    case 'openai':
    case 'image-generation':
      return buildGenericProviderOptions('openai-compatible', assistant, model, capabilities)
  }

  // Fallback: model-name heuristic (covers Vercel Gateway, AiHubMix, and untagged models)
  if (isAnthropicModel(model)) {
    return buildAnthropicProviderOptions(assistant, model, capabilities)
  } else if (isOpenAIModel(model)) {
    return buildOpenAIProviderOptions(assistant, model, capabilities, serviceTier, textVerbosity)
  } else if (isGeminiModel(model)) {
    return buildGeminiProviderOptions(assistant, model, capabilities)
  } else if (isGrokModel(model)) {
    return buildXAIProviderOptions(assistant, model, capabilities)
  } else {
    return buildGenericProviderOptions('openai-compatible', assistant, model, capabilities)
  }
}
