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 { XaiResponsesProviderOptions } from '@ai-sdk/xai'
import type OpenAI from '@cherrystudio/openai'
import type { OpenAIReasoningEffort } from '@cherrystudio/provider-registry'
import { loggerService } from '@logger'
import { DEFAULT_MAX_TOKENS } from '@main/ai/constants'
import {
  computeBudgetTokens,
  FALLBACK_TOKEN_LIMIT,
  getThinkingBudget as sharedGetThinkingBudget
} from '@shared/ai/reasoningBudget'
import type { Assistant } from '@shared/data/types/assistant'
import type { Model } from '@shared/data/types/model'
import { parseUniqueModelId } from '@shared/data/types/model'
import type { Provider } from '@shared/data/types/provider'
import type { OpenAIReasoningSummary, ReasoningEffortOption } from '@shared/types/aiSdk'
import {
  findTokenLimit,
  GEMINI_FLASH_MODEL_REGEX,
  getLowerBaseModelName,
  getModelSupportedReasoningEffortOptions,
  isClaude46SeriesModel,
  isClaude47SeriesModel,
  isDeepSeekHybridInferenceModel,
  isDeepSeekV4PlusModel,
  isDoubaoSeed18Model,
  isDoubaoSeedAfter251015,
  isDoubaoThinkingAutoModel,
  isGemini3ThinkingTokenModel,
  isGrok4FastReasoningModel,
  isHostedGemma4ThinkingModel,
  isOpenAIDeepResearchModel,
  isOpenAIModel,
  isOpenAIOpenWeightModel,
  isOpenAIReasoningModel,
  isQwen35to39Model,
  isQwenAlwaysThinkModel,
  isQwenReasoningModel,
  isReasoningModel,
  isSupportedReasoningEffortGrokModel,
  isSupportedReasoningEffortModel,
  isSupportedReasoningEffortOpenAIModel,
  isSupportedThinkingTokenClaudeModel,
  isSupportedThinkingTokenDoubaoModel,
  isSupportedThinkingTokenGeminiModel,
  isSupportedThinkingTokenHunyuanModel,
  isSupportedThinkingTokenKimiModel,
  isSupportedThinkingTokenMiMoModel,
  isSupportedThinkingTokenModel,
  isSupportedThinkingTokenQwenModel,
  isSupportedThinkingTokenZhipuModel,
  isSupportNoneReasoningEffortModel
} from '@shared/utils/model'
import { isSupportEnableThinkingProvider } from '@shared/utils/provider'
import { EFFORT_RATIO } from '@shared/utils/reasoning'
import { isSystemProviderId, SystemProviderIds } from '@shared/utils/systemProviderId'
import { toInteger } from 'lodash'
import type { OllamaProviderOptions } from 'ollama-ai-provider-v2'

const logger = loggerService.withContext('reasoning')

type ReasoningEffortOptionalParams = {
  thinking?: { type: 'disabled' | 'enabled' | 'auto'; budget_tokens?: number }
  reasoning?: { max_tokens?: number; exclude?: boolean; effort?: string; enabled?: boolean } | OpenAI.Reasoning
  reasoningEffort?: ReasoningEffortOption
  // WARN: This field will be overwrite to undefined by aisdk if the provider is openai-compatible. Use reasoningEffort instead.
  reasoning_effort?: ReasoningEffortOption
  enable_thinking?: boolean
  thinking_budget?: number
  incremental_output?: boolean
  enable_reasoning?: boolean
  // nvidia, etc.
  chat_template_kwargs?: {
    thinking?: boolean
    enable_thinking?: boolean
    thinking_budget?: number
  }
  extra_body?: {
    google?: {
      thinking_config: {
        thinking_budget: number
        include_thoughts?: boolean
      }
    }
    thinking?: {
      type: 'enabled' | 'disabled'
    }
    thinking_budget?: number
    reasoning_effort?: ReasoningEffortOption
  }
  disable_reasoning?: boolean
  // Add any other potential reasoning-related keys here if they exist
}

// The function is only for generic provider. May extract some logics to independent provider
export function getReasoningEffort(
  assistant: Assistant,
  model: Model,
  provider: Provider
): ReasoningEffortOptionalParams {
  const rawModelId = parseUniqueModelId(model.id).modelId
  const modelId = getLowerBaseModelName(rawModelId)
  if (provider.id === 'groq') {
    return {}
  }

  if (!isReasoningModel(model)) {
    return {}
  }

  if (isOpenAIDeepResearchModel(model)) {
    return {
      reasoning_effort: 'medium'
    }
  }
  const reasoningEffort = assistant?.settings?.reasoning_effort as ReasoningEffortOption | undefined

  // reasoningEffort is not set, no extra reasoning setting
  // Generally, for every model which supports reasoning control, the reasoning effort won't be undefined.
  // It's for some reasoning models that don't support reasoning control, such as deepseek reasoner.
  if (!reasoningEffort || reasoningEffort === 'default') {
    return {}
  }

  // Handle 'none' reasoningEffort. It's explicitly off.
  if (reasoningEffort === 'none') {
    // openrouter: use reasoning
    if (model.providerId === SystemProviderIds.openrouter) {
      if (isSupportNoneReasoningEffortModel(model) && reasoningEffort === 'none') {
        return { reasoning: { effort: 'none' } }
      }
      return { reasoning: { enabled: false, exclude: true } }
    }

    // nvidia: must use chat_template_kwargs
    // Since limited documentation, it's hard to find what parameters should be set
    // only part of mainstream oss model covered, all verified by nvidia api
    if (model.providerId === SystemProviderIds.nvidia) {
      if (isSupportedThinkingTokenQwenModel(model)) {
        return { chat_template_kwargs: { enable_thinking: false } }
      } else if (isDeepSeekHybridInferenceModel(model)) {
        return { chat_template_kwargs: { thinking: false } }
      } else if (isSupportedThinkingTokenKimiModel(model)) {
        return { chat_template_kwargs: { thinking: false } }
      } else if (isSupportedThinkingTokenZhipuModel(model)) {
        return { chat_template_kwargs: { enable_thinking: false } }
      }
    }

    // providers that use enable_thinking
    if (
      (isSupportEnableThinkingProvider(provider) &&
        (isSupportedThinkingTokenQwenModel(model) || isSupportedThinkingTokenHunyuanModel(model))) ||
      (provider.id === SystemProviderIds.dashscope &&
        (isDeepSeekHybridInferenceModel(model) ||
          isSupportedThinkingTokenZhipuModel(model) ||
          isSupportedThinkingTokenKimiModel(model))) ||
      // SiliconFlow uses enable_thinking for DeepSeek and Zhipu models, same as positive path
      (provider.id === SystemProviderIds.silicon &&
        (isDeepSeekHybridInferenceModel(model) || isSupportedThinkingTokenZhipuModel(model)))
    ) {
      return { enable_thinking: false }
    }

    // together
    if (provider.id === SystemProviderIds.together) {
      return { reasoning: { enabled: false } }
    }

    // gemini
    if (isSupportedThinkingTokenGeminiModel(model)) {
      if (GEMINI_FLASH_MODEL_REGEX.test(model.id)) {
        return {
          extra_body: {
            google: {
              thinking_config: {
                thinking_budget: 0
              }
            }
          }
        }
      } else {
        logger.warn(`Model ${model.id} cannot disable reasoning. Fallback to empty reasoning param.`)
        return {}
      }
    }

    // use thinking, doubao, zhipu, etc.
    if (
      isSupportedThinkingTokenDoubaoModel(model) ||
      isSupportedThinkingTokenZhipuModel(model) ||
      isSupportedThinkingTokenKimiModel(model)
    ) {
      if (provider.id === SystemProviderIds.cerebras) {
        return {
          disable_reasoning: true
        }
      }
      return { thinking: { type: 'disabled' } }
    }

    // DeepSeek V4+ defaults to thinking enabled, explicitly disable it
    if (isDeepSeekV4PlusModel(model)) {
      return { thinking: { type: 'disabled' } }
    }

    // DeepSeek V3.x hybrid, default behavior is non-thinking
    if (isDeepSeekHybridInferenceModel(model)) {
      return {}
    }

    // GPT 5.1, GPT 5.2, or newer
    if (isSupportNoneReasoningEffortModel(model)) {
      return {
        reasoningEffort: 'none'
      }
    }

    // Qwen 3.5 without direct enable_thinking
    // https://huggingface.co/Qwen/Qwen3.5-397B-A17B#instruct-or-non-thinking-mode
    if (isQwen35to39Model(model)) {
      return {
        chat_template_kwargs: {
          enable_thinking: false
        }
      }
    }

    // Mistral Small models: reasoningEffort 'none'
    if (modelId.includes('mistral-small-2603')) {
      return { reasoningEffort: 'none' }
    }

    logger.warn(`Model ${model.id} doesn't match any disable reasoning behavior. Fallback to empty reasoning param.`)
    return {}
  }

  // reasoningEffort有效的情况
  // https://creator.poe.com/docs/external-applications/openai-compatible-api#additional-considerations
  // Poe provider - supports custom bot parameters via extra_body
  if (provider.id === SystemProviderIds.poe) {
    if (isOpenAIReasoningModel(model)) {
      return {
        extra_body: {
          reasoning_effort: reasoningEffort === 'auto' ? 'medium' : reasoningEffort
        }
      }
    }

    // Claude models use thinking_budget parameter in extra_body
    if (isSupportedThinkingTokenClaudeModel(model)) {
      const effortRatio = EFFORT_RATIO[reasoningEffort]
      const tokenLimit = findTokenLimit(rawModelId)
      const maxTokens = assistant.settings?.maxTokens

      if (!tokenLimit) {
        logger.warn(
          `No token limit configuration found for Claude model "${model.id}" on Poe provider. ` +
            `Reasoning effort setting "${reasoningEffort}" will not be applied.`
        )
        return {}
      }

      let budgetTokens = Math.floor((tokenLimit.max - tokenLimit.min) * effortRatio + tokenLimit.min)
      budgetTokens = Math.floor(Math.max(1024, Math.min(budgetTokens, (maxTokens || DEFAULT_MAX_TOKENS) * effortRatio)))

      return {
        extra_body: {
          thinking_budget: budgetTokens
        }
      }
    }

    // Gemini models use thinking_budget parameter in extra_body
    if (isSupportedThinkingTokenGeminiModel(model)) {
      const effortRatio = EFFORT_RATIO[reasoningEffort]
      const tokenLimit = findTokenLimit(rawModelId)
      let budgetTokens: number | undefined
      if (tokenLimit && reasoningEffort !== 'auto') {
        budgetTokens = Math.floor((tokenLimit.max - tokenLimit.min) * effortRatio + tokenLimit.min)
      } else if (!tokenLimit && reasoningEffort !== 'auto') {
        logger.warn(
          `No token limit configuration found for Gemini model "${model.id}" on Poe provider. ` +
            `Using auto (-1) instead of requested effort "${reasoningEffort}".`
        )
      }
      return {
        extra_body: {
          thinking_budget: budgetTokens ?? -1
        }
      }
    }

    // Poe reasoning model not in known categories (GPT-5, Claude, Gemini)
    logger.warn(
      `Poe provider reasoning model "${model.id}" does not match known categories ` +
        `(GPT-5, Claude, Gemini). Reasoning effort setting "${reasoningEffort}" will not be applied.`
    )
    return {}
  }

  // OpenRouter models
  if (model.providerId === SystemProviderIds.openrouter) {
    // Grok 4 Fast doesn't support effort levels, always use enabled: true
    if (isGrok4FastReasoningModel(model)) {
      return {
        reasoning: {
          enabled: true // Ignore effort level, just enable reasoning
        }
      }
    }

    // Other OpenRouter models that support effort levels
    if (isSupportedReasoningEffortModel(model) || isSupportedThinkingTokenModel(model)) {
      return {
        reasoning: {
          effort: reasoningEffort === 'auto' ? 'medium' : reasoningEffort
        }
      }
    }
  }

  const effortRatio = EFFORT_RATIO[reasoningEffort]
  const tokenLimit = findTokenLimit(modelId)
  let budgetTokens: number | undefined
  if (tokenLimit) {
    budgetTokens = Math.floor((tokenLimit.max - tokenLimit.min) * effortRatio + tokenLimit.min)
  }

  // nvidia: must use chat_template_kwargs
  // Since limited documentation, it's hard to find what parameters should be set
  // only part of mainstream oss model covered, all verified by nvidia api
  if (model.providerId === SystemProviderIds.nvidia) {
    if (isSupportedThinkingTokenQwenModel(model)) {
      const enableThinkingConfig = isQwenAlwaysThinkModel(model) ? {} : { enable_thinking: true }
      return {
        chat_template_kwargs: {
          ...enableThinkingConfig,
          thinking_budget: budgetTokens
        }
      }
    } else if (isDeepSeekHybridInferenceModel(model)) {
      return { chat_template_kwargs: { thinking: true } }
    } else if (isSupportedThinkingTokenKimiModel(model)) {
      return { chat_template_kwargs: { thinking: true } }
    } else if (isSupportedThinkingTokenZhipuModel(model)) {
      return { chat_template_kwargs: { enable_thinking: true } }
    }
  }

  // See https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions
  if (model.providerId === SystemProviderIds.silicon) {
    if (
      isDeepSeekHybridInferenceModel(model) ||
      isSupportedThinkingTokenZhipuModel(model) ||
      isSupportedThinkingTokenQwenModel(model) ||
      isSupportedThinkingTokenHunyuanModel(model)
    ) {
      return {
        enable_thinking: true,
        // Hard-encoded maximum, only for silicon
        thinking_budget: budgetTokens ? toInteger(Math.max(budgetTokens, 32768)) : undefined
      }
    }
    return {}
  }

  // DeepSeek V4+ models support reasoning_effort: "high" | "max" alongside thinking control
  // UI uses "xhigh" which maps to API's "max"; all other effort levels map to "high"
  if (isDeepSeekV4PlusModel(model)) {
    return {
      thinking: { type: 'enabled' as const },
      reasoning_effort: reasoningEffort === 'xhigh' ? ('max' as OpenAIReasoningEffort) : 'high'
    }
  }

  // DeepSeek hybrid inference models, v3.1 and maybe more in the future
  // 不同的 provider 有不同的思考控制方式，在这里统一解决
  if (isDeepSeekHybridInferenceModel(model)) {
    if (isSystemProviderId(provider.id)) {
      switch (provider.id) {
        case SystemProviderIds.dashscope:
          return {
            enable_thinking: true,
            incremental_output: true
          }
        // TODO: 支持 new-api类型
        case SystemProviderIds['new-api']:
        case SystemProviderIds.cherryin: {
          return {
            extra_body: {
              thinking: {
                type: 'enabled' // auto is invalid
              }
            }
          }
        }
        case SystemProviderIds.hunyuan:
        case SystemProviderIds['tencent-cloud-ti']:
        case SystemProviderIds.doubao:
        case SystemProviderIds.deepseek:
        case SystemProviderIds.aihubmix:
        case SystemProviderIds.sophnet:
        case SystemProviderIds.ppio:
        case SystemProviderIds.dmxapi:
          return {
            thinking: {
              type: 'enabled' // auto is invalid
            }
          }
        case SystemProviderIds.openrouter:
        case SystemProviderIds.together:
          return {
            reasoning: {
              enabled: true
            }
          }
        default:
          break
      }
    }
    logger.warn(
      `Use default thinking options for provider ${provider.name} as DeepSeek v3.1+ thinking control method is unknown`
    )
    return {
      thinking: {
        type: 'enabled'
      }
    }
  }

  // OpenRouter models, use reasoning
  // FIXME: duplicated openrouter handling. remove one
  if (model.providerId === SystemProviderIds.openrouter) {
    if (isSupportedReasoningEffortModel(model) || isSupportedThinkingTokenModel(model)) {
      return {
        reasoning: {
          effort: reasoningEffort === 'auto' ? 'medium' : reasoningEffort
        }
      }
    }
  }

  // https://help.aliyun.com/zh/model-studio/deep-thinking
  if (provider.id === SystemProviderIds.dashscope) {
    // For dashscope: Qwen, DeepSeek, and GLM models use enable_thinking to control thinking
    // No effort, only on/off
    if (
      isQwenReasoningModel(model) ||
      isSupportedThinkingTokenZhipuModel(model) ||
      isSupportedThinkingTokenKimiModel(model)
    ) {
      return {
        enable_thinking: true,
        thinking_budget: budgetTokens
      }
    }
  }

  // https://docs.together.ai/reference/chat-completions-1#body-reasoning-effort
  if (provider.id === SystemProviderIds.together) {
    let adjustedReasoningEffort: 'low' | 'medium' | 'high' = 'medium'
    switch (reasoningEffort) {
      case 'minimal':
        adjustedReasoningEffort = 'low'
        break
      case 'xhigh':
        adjustedReasoningEffort = 'high'
        break
      case 'auto':
        adjustedReasoningEffort = 'medium'
        break
      default:
        adjustedReasoningEffort = reasoningEffort
        break
    }
    return {
      // Only low, medium, high
      reasoningEffort: adjustedReasoningEffort,
      reasoning: { enabled: true }
    }
  }

  // Qwen models, use enable_thinking
  if (isQwenReasoningModel(model)) {
    const supportEnableThinking = isSupportEnableThinkingProvider(provider)
    const enableThinkingConfig = isQwenAlwaysThinkModel(model) ? {} : { enable_thinking: true }
    if (supportEnableThinking) {
      return {
        ...enableThinkingConfig,
        thinking_budget: budgetTokens
      }
    } else {
      return {
        chat_template_kwargs: {
          ...enableThinkingConfig,
          thinking_budget: budgetTokens
        }
      }
    }
  }

  // Hunyuan models, use enable_thinking
  if (isSupportedThinkingTokenHunyuanModel(model) && isSupportEnableThinkingProvider(provider)) {
    return {
      enable_thinking: true
    }
  }

  // Grok models/Perplexity models/OpenAI models, use reasoning_effort
  if (isSupportedReasoningEffortModel(model)) {
    // 检查模型是否支持所选选项
    const supportedOptions = getModelSupportedReasoningEffortOptions(model)?.filter((option) => option !== 'default')
    if (supportedOptions?.includes(reasoningEffort)) {
      return {
        reasoningEffort
      }
    } else {
      // 如果不支持，fallback到第一个支持的值
      return {
        reasoningEffort: supportedOptions?.[0] as ReasoningEffortOption | undefined
      }
    }
  }

  // Mistral Small models use reasoningEffort with 'none' | 'high'
  if (modelId.includes('mistral-small-2603')) {
    return { reasoningEffort: 'high' }
  }

  // gemini series, openai compatible api
  if (isSupportedThinkingTokenGeminiModel(model)) {
    // https://ai.google.dev/gemini-api/docs/gemini-3?thinking=high#openai_compatibility
    if (isGemini3ThinkingTokenModel(model)) {
      return {
        reasoningEffort
      }
    }
    if (reasoningEffort === 'auto') {
      return {
        extra_body: {
          google: {
            thinking_config: {
              thinking_budget: -1,
              include_thoughts: true
            }
          }
        }
      }
    }
    return {
      extra_body: {
        google: {
          thinking_config: {
            thinking_budget: budgetTokens ?? -1,
            include_thoughts: true
          }
        }
      }
    }
  }

  // Claude models, openai compatible api
  if (isSupportedThinkingTokenClaudeModel(model)) {
    const maxTokens = assistant.settings?.maxTokens
    return {
      thinking: {
        type: 'enabled',
        budget_tokens: budgetTokens
          ? Math.floor(Math.max(1024, Math.min(budgetTokens, (maxTokens || DEFAULT_MAX_TOKENS) * effortRatio)))
          : undefined
      }
    }
  }

  // Use thinking, doubao, zhipu, etc.
  if (isSupportedThinkingTokenDoubaoModel(model)) {
    if (isDoubaoSeedAfter251015(model) || isDoubaoSeed18Model(model)) {
      return { reasoningEffort }
    }
    if (reasoningEffort === 'high') {
      return { thinking: { type: 'enabled' } }
    }
    if (reasoningEffort === 'auto' && isDoubaoThinkingAutoModel(model)) {
      return { thinking: { type: 'auto' } }
    }
    // 其他情况不带 thinking 字段
    return {}
  }
  if (isSupportedThinkingTokenZhipuModel(model)) {
    if (provider.id === SystemProviderIds.cerebras) {
      return {}
    }
    return { thinking: { type: 'enabled' } }
  }

  if (isSupportedThinkingTokenMiMoModel(model) || isSupportedThinkingTokenKimiModel(model)) {
    return {
      thinking: { type: 'enabled' }
    }
  }

  // Default case: no special thinking settings
  return {}
}

/**
 * Get OpenAI reasoning parameters. For official OpenAI provider only.
 */
export function getOpenAIReasoningParams(
  assistant: Assistant,
  model: Model,
  openAISettings?: { summaryText?: OpenAIReasoningSummary }
): Pick<OpenAIResponsesProviderOptions, 'reasoningEffort' | 'reasoningSummary'> {
  if (!isReasoningModel(model)) {
    return {}
  }

  let reasoningEffort = assistant?.settings?.reasoning_effort

  if (!reasoningEffort || reasoningEffort === 'default') {
    return {}
  }

  if (isOpenAIDeepResearchModel(model) || reasoningEffort === 'auto') {
    reasoningEffort = 'medium'
  }

  // 非OpenAI模型，但是Provider类型是responses/azure openai的情况
  if (!isOpenAIModel(model)) {
    return {
      reasoningEffort
    }
  }

  const summaryText = openAISettings?.summaryText

  let reasoningSummary: OpenAIReasoningSummary = undefined

  if (model.id.includes('o1-pro')) {
    reasoningSummary = undefined
  } else {
    reasoningSummary = summaryText
  }

  // OpenAI 推理参数
  if (isSupportedReasoningEffortOpenAIModel(model)) {
    return {
      reasoningEffort,
      reasoningSummary
    }
  }

  return {}
}

/**
 * Main-side wrapper around the shared `getThinkingBudget` — fixes the
 * `effortRatioMap` argument so internal callers don't pass it on every
 * call. Uses the strict (no-fallback) variant: unknown models return
 * `undefined`. The renderer Code page calls the shared function directly
 * with `{ fallbackOnUnknown: true }`.
 */
export function getThinkingBudget(
  maxTokens: number | undefined,
  reasoningEffort: string | undefined,
  modelId: string
): number | undefined {
  return sharedGetThinkingBudget(maxTokens, reasoningEffort, modelId, EFFORT_RATIO)
}

// Compute a fallback budgetTokens using a conservative token limit when
// findTokenLimit() cannot determine the model's actual limit. This ensures
// { type: 'enabled' } always carries a valid budget, which is required by
// the Claude Agent SDK and the Anthropic Messages API.
function getFallbackBudgetTokens(reasoningEffort: string | undefined): number {
  const effortRatio = EFFORT_RATIO[reasoningEffort ?? 'high'] ?? EFFORT_RATIO.high
  return computeBudgetTokens(FALLBACK_TOKEN_LIMIT, effortRatio)
}

/**
 * Get Anthropic reasoning parameters.
 *
 * Returns different parameter shapes depending on the model:
 * - **Claude 4.6**: `{ thinking: { type: 'adaptive' }, effort: 'low' | 'medium' | 'high' | 'max' }`
 *   Uses the new adaptive thinking API with effort-based control.
 * - **Other Claude models** (4.0, 4.1, 4.5, etc.): `{ thinking: { type: 'enabled', budgetTokens: number } }`
 *   Uses the classic thinking API with explicit token budget.
 * - **Non-Anthropic models served via the Claude-compatible endpoint** (Kimi, MiniMax,
 *   DeepSeek V4+, etc.): `{ thinking: { type: 'enabled', budgetTokens: number }, sendReasoning: true, effort? }`
 *   `sendReasoning: true` ensures reasoning output is streamed back to the UI.
 *   `effort` is only added for DeepSeek V4+ (`high` | `xhigh` → `high` | `max`).
 */
export function getAnthropicReasoningParams(
  assistant: Assistant,
  model: Model
): {
  thinking?: AnthropicProviderOptions['thinking']
  effort?: AnthropicProviderOptions['effort']
  sendReasoning?: AnthropicProviderOptions['sendReasoning']
} {
  if (!isReasoningModel(model)) {
    return {}
  }

  const reasoningEffort = assistant?.settings?.reasoning_effort

  if (!reasoningEffort || reasoningEffort === 'default') {
    return {}
  }

  if (reasoningEffort === 'none') {
    return {
      thinking: {
        type: 'disabled'
      }
    }
  }

  // Claude reasoning parameters
  if (isSupportedThinkingTokenClaudeModel(model)) {
    // Claude 4.7: adaptive thinking + native 'xhigh' effort.
    // Also requires thinking.display: 'summarized' — API defaults to 'omitted'
    // (no reasoning text in response), which would break Cherry's thinking UI.
    if (isClaude47SeriesModel(model)) {
      const effort47Map = {
        default: undefined,
        auto: undefined,
        minimal: 'low',
        low: 'low',
        medium: 'medium',
        high: 'high',
        xhigh: 'xhigh'
      } as const satisfies Record<Exclude<ReasoningEffortOption, 'none'>, AnthropicProviderOptions['effort']>
      const effort = effort47Map[reasoningEffort]
      const thinking = { type: 'adaptive', display: 'summarized' } as const
      return effort ? { thinking, effort } : { thinking }
    }

    // Claude 4.6 uses adaptive thinking + effort parameters
    // Map reasoningEffort to Claude 4.6 supported effort values
    if (isClaude46SeriesModel(model)) {
      // Claude 4.6 supports: low, medium, high, max
      // Mapping rules: default/none -> no effort (uses default high)
      //                minimal/low -> low
      //                medium -> medium
      //                high -> high
      //                xhigh -> max
      const effortMap = {
        default: undefined,
        auto: undefined,
        minimal: 'low',
        low: 'low',
        medium: 'medium',
        high: 'high',
        xhigh: 'max'
      } as const satisfies Record<Exclude<ReasoningEffortOption, 'none'>, AnthropicProviderOptions['effort']>
      const effort = effortMap[reasoningEffort]
      return effort ? { thinking: { type: 'adaptive' }, effort } : { thinking: { type: 'adaptive' } }
    }

    // Other Claude models continue using enabled + budgetTokens
    const maxTokens = assistant.settings?.maxTokens
    const budgetTokens = getThinkingBudget(maxTokens, reasoningEffort, model.id)

    return {
      thinking: {
        type: 'enabled',
        budgetTokens: budgetTokens ?? getFallbackBudgetTokens(reasoningEffort)
      }
    }
  } else {
    // 其他使用claude端點的模型，比如Kimi,Minimax等等
    const maxTokens = assistant.settings?.maxTokens
    const budgetTokens = getThinkingBudget(maxTokens, reasoningEffort, model.id)
    const params: Partial<ReturnType<typeof getAnthropicReasoningParams>> = {
      thinking: {
        type: 'enabled',
        budgetTokens: budgetTokens ?? getFallbackBudgetTokens(reasoningEffort)
      },
      sendReasoning: true
    }
    // https://api-docs.deepseek.com/guides/thinking_mode
    // DeepSeek V4+ exposes only 'high' and 'xhigh' as user-facing effort levels
    // (see MODEL_SUPPORTED_REASONING_EFFORT.deepseek_v4); default/none are already
    // short-circuited earlier in this function. The explicit map avoids silently
    // downgrading future levels (low/medium/auto) to 'high' — unmapped values are
    // simply omitted so callers fall back to API defaults instead.
    if (isDeepSeekV4PlusModel(model)) {
      const deepSeekV4EffortMap = {
        high: 'high',
        xhigh: 'max'
      } as const
      const effort = deepSeekV4EffortMap[reasoningEffort as keyof typeof deepSeekV4EffortMap]
      if (effort) {
        params.effort = effort
      }
    }
    // Always include budgetTokens to prevent Claude Agent SDK from converting
    // { type: 'enabled' } into '--thinking adaptive', which non-Anthropic
    // upstream providers do not support (they only accept 'enabled'/'disabled').
    return params
  }
}

type GoogleThinkingLevel = NonNullable<GoogleGenerativeAIProviderOptions['thinkingConfig']>['thinkingLevel']

function mapToGeminiThinkingLevel(reasoningEffort: ReasoningEffortOption): GoogleThinkingLevel {
  switch (reasoningEffort) {
    case 'auto':
    case 'default':
      return undefined
    case 'none':
      return 'minimal'
    case 'minimal':
      return 'minimal'
    case 'low':
      return 'low'
    case 'medium':
      return 'medium'
    case 'high':
    case 'xhigh':
      return 'high'
    default:
      // Enforce all possible values are handled
      reasoningEffort satisfies never
      return undefined
  }
}

/**
 * 获取 Gemini 推理参数
 * 从 GeminiAPIClient 中提取的逻辑
 * 注意：Gemini/GCP 端点所使用的 thinkingBudget 等参数应该按照驼峰命名法传递
 * 而在 Google 官方提供的 OpenAI 兼容端点中则使用蛇形命名法 thinking_budget
 */
export function getGeminiReasoningParams(
  assistant: Assistant,
  model: Model
): Pick<GoogleGenerativeAIProviderOptions, 'thinkingConfig'> {
  if (!isReasoningModel(model) || !isSupportedThinkingTokenGeminiModel(model)) {
    return {}
  }

  const reasoningEffort = assistant?.settings?.reasoning_effort as ReasoningEffortOption | undefined

  if (!reasoningEffort || reasoningEffort === 'default') {
    return {}
  }

  const rawModelId = parseUniqueModelId(model.id).modelId

  let thinkingLevel: GoogleThinkingLevel | null = null
  const includeThoughts = reasoningEffort !== 'none'

  if (isHostedGemma4ThinkingModel(model)) {
    // Hosted Gemma 4 does not expose a distinct hard-off mode on the Gemini API.
    // We only surface minimal/high in the UI and collapse legacy or unexpected
    // `none` inputs to `minimal` for compatibility.
    const isHighThinking = reasoningEffort === 'high' || reasoningEffort === 'xhigh'
    thinkingLevel = isHighThinking ? 'high' : 'minimal'

    return {
      thinkingConfig: {
        includeThoughts: isHighThinking,
        thinkingLevel
      }
    }
  }

  // https://ai.google.dev/gemini-api/docs/gemini-3?thinking=high#new_api_features_in_gemini_3
  if (isGemini3ThinkingTokenModel(model)) {
    thinkingLevel = mapToGeminiThinkingLevel(reasoningEffort)
    if (thinkingLevel === 'minimal' && getLowerBaseModelName(rawModelId).includes('pro')) {
      thinkingLevel = 'low'
    }
  }

  if (thinkingLevel !== null) {
    // Gemini 3 branch. thinkingLevel can be undefined (auto) or a specific level.
    return {
      thinkingConfig: {
        includeThoughts,
        thinkingLevel
      }
    }
  } else {
    // Old models
    const effortRatio = EFFORT_RATIO[reasoningEffort]

    if (reasoningEffort === 'auto') {
      return {
        thinkingConfig: {
          includeThoughts,
          thinkingBudget: -1
        }
      }
    }

    if (reasoningEffort === 'none') {
      return {
        thinkingConfig: {
          includeThoughts,
          ...(GEMINI_FLASH_MODEL_REGEX.test(model.id) ? { thinkingBudget: 0 } : {})
        }
      }
    }

    const { min, max } = findTokenLimit(rawModelId) || { min: 0, max: 0 }
    const budget = Math.floor((max - min) * effortRatio + min)

    return {
      thinkingConfig: {
        includeThoughts,
        ...(budget > 0 ? { thinkingBudget: budget } : {})
      }
    }
  }
}

/**
 * Get XAI-specific reasoning parameters
 * This function should only be called for XAI provider models
 * @param assistant - The assistant configuration
 * @param model - The model being used
 * @returns XAI-specific reasoning parameters
 */
export function getXAIReasoningParams(
  assistant: Assistant,
  model: Model
): Pick<XaiResponsesProviderOptions, 'reasoningEffort'> {
  const isGrok43 =
    getLowerBaseModelName(model.id).includes('grok-4.3') && !getLowerBaseModelName(model.id).includes('non-reasoning')

  if (!isSupportedReasoningEffortGrokModel(model) && !isGrok43) {
    return {}
  }

  const reasoningEffort = assistant.settings?.reasoning_effort
  if (!reasoningEffort || reasoningEffort === 'default') return {}

  if (isGrok43) {
    switch (reasoningEffort) {
      case 'none':
      case 'low':
      case 'medium':
      case 'high':
        return { reasoningEffort }
      default:
        // grok-4.3 accepts none/low/medium/high — the xAI responses `reasoningEffort` enum, extended
        // to include 'none' by #15137's `@ai-sdk/xai@3.0.83` patch (still in `patches/`); 'none'
        // disables reasoning. Genuinely out-of-range values (auto/minimal/xhigh) are dropped — trace
        // them so the omission is diagnosable.
        logger.debug('grok-4.3 dropping unsupported reasoning effort', { reasoningEffort })
        return {}
    }
  }

  // Legacy grok models (grok-3-mini, openrouter/grok-4-fast): constrained effort mapping
  switch (reasoningEffort) {
    case 'auto':
    case 'minimal':
    case 'medium':
      return { reasoningEffort: 'low' }
    case 'low':
    case 'high':
      return { reasoningEffort }
    case 'xhigh':
      return { reasoningEffort: 'high' }
    default:
      return {}
  }
}

/**
 * Get Bedrock reasoning parameters
 */
export function getBedrockReasoningParams(
  assistant: Assistant,
  model: Model
): Pick<BedrockProviderOptions, 'reasoningConfig'> {
  if (!isReasoningModel(model)) {
    return {}
  }

  const reasoningEffort = assistant?.settings?.reasoning_effort

  if (reasoningEffort === undefined || reasoningEffort === 'default') {
    return {}
  }

  if (reasoningEffort === 'none') {
    return {
      reasoningConfig: {
        type: 'disabled'
      }
    }
  }

  // Only apply thinking budget for Claude reasoning models
  if (!isSupportedThinkingTokenClaudeModel(model)) {
    return {}
  }

  // Claude 4.6 / 4.7 use adaptive thinking + maxReasoningEffort.
  // Bedrock's maxReasoningEffort enum doesn't yet include 'xhigh', so 4.7 xhigh
  // falls back to 'max' here (matches the 4.6 mapping).
  if (isClaude46SeriesModel(model) || isClaude47SeriesModel(model)) {
    const effortMap = {
      auto: undefined,
      minimal: 'low',
      low: 'low',
      medium: 'medium',
      high: 'high',
      xhigh: 'max'
    } as const satisfies Record<
      Exclude<ReasoningEffortOption, 'none' | 'default'>,
      NonNullable<BedrockProviderOptions['reasoningConfig']>['maxReasoningEffort']
    >
    const maxReasoningEffort = effortMap[reasoningEffort]
    return maxReasoningEffort
      ? { reasoningConfig: { type: 'adaptive', maxReasoningEffort } }
      : { reasoningConfig: { type: 'adaptive' } }
  }

  // Other Claude models use enabled + budgetTokens
  const maxTokens = assistant.settings?.maxTokens
  const budgetTokens = getThinkingBudget(maxTokens, reasoningEffort, model.id)
  return {
    reasoningConfig: {
      type: 'enabled',
      budgetTokens: budgetTokens
    }
  }
}

/**
 * Get Ollama reasoning parameters
 * Handles the `think` parameter for Ollama models
 *
 * - GPT-OSS models: accept 'low' | 'medium' | 'high' string values
 * - Other models: boolean only (true/false)
 */
export function getOllamaReasoningParams(assistant: Assistant, model: Model): Pick<OllamaProviderOptions, 'think'> {
  const reasoningEffort = assistant.settings?.reasoning_effort

  if (isOpenAIOpenWeightModel(model)) {
    // gpt-oss models accept 'low' | 'medium' | 'high' string values
    if (reasoningEffort === 'low' || reasoningEffort === 'medium' || reasoningEffort === 'high') {
      return { think: reasoningEffort }
    } else if (reasoningEffort === 'none') {
      return { think: false }
    }
    return { think: true }
  }

  // Other models: boolean only. undefined defaults to true (user enabled reasoning)
  return { think: reasoningEffort !== 'none' }
}

/**
 * 获取自定义参数
 * 从 assistant 设置中提取自定义参数
 */
export function getCustomParameters(assistant: Assistant): Record<string, any> {
  return (
    assistant?.settings?.customParameters?.reduce((acc, param) => {
      if (!param.name?.trim()) {
        return acc
      }
      // Parse JSON type parameters
      // The UI stores JSON type params as strings (e.g., '{"key":"value"}'),
      // so parse them into objects before sending to the API.
      if (param.type === 'json') {
        const value = param.value as string
        if (value === 'undefined') {
          return { ...acc, [param.name]: undefined }
        }
        try {
          return { ...acc, [param.name]: JSON.parse(value) }
        } catch {
          return { ...acc, [param.name]: value }
        }
      }
      return {
        ...acc,
        [param.name]: param.value
      }
    }, {}) || {}
  )
}

/**
 * Get reasoning tag name based on model ID
 * Used for extractReasoningMiddleware configuration
 */
export function getReasoningTagName(modelId: string | undefined): string {
  const tagName = {
    reasoning: 'reasoning',
    think: 'think',
    thought: 'thought',
    seedThink: 'seed:think'
  }

  if (modelId?.includes('gpt-oss')) return tagName.reasoning
  if (modelId?.includes('gemini')) return tagName.thought
  if (modelId?.includes('seed-oss-36b')) return tagName.seedThink
  return tagName.think
}
