import type OpenAI from '@cherrystudio/openai'
import { isEmbeddingModel, isRerankModel } from '@renderer/config/models/embedding'
import type { Assistant } from '@renderer/types'
import { type Model, SystemProviderIds } from '@renderer/types'
import type { OpenAIVerbosity, ValidOpenAIVerbosity } from '@renderer/types/aiCoreTypes'
import { getLowerBaseModelName } from '@renderer/utils'

import {
  isGPT5ProModel,
  isGPT5SeriesModel,
  isGPT51SeriesModel,
  isGPT52SeriesModel,
  isOpenAIChatCompletionOnlyModel,
  isOpenAIOpenWeightModel,
  isOpenAIReasoningModel,
  isSupportVerbosityModel
} from './openai'
import { isQwenMTModel } from './qwen'
import { isClaude45ReasoningModel } from './reasoning'
import { isGenerateImageModel, isTextToImageModel, isVisionModel } from './vision'
export const NOT_SUPPORTED_REGEX = /(?:^tts|whisper|speech)/i
export const GEMINI_FLASH_MODEL_REGEX = new RegExp('gemini.*-flash.*$', 'i')

export function isSupportFlexServiceTierModel(model: Model): boolean {
  if (!model) {
    return false
  }
  const modelId = getLowerBaseModelName(model.id)
  return (
    (modelId.includes('o3') && !modelId.includes('o3-mini')) || modelId.includes('o4-mini') || modelId.includes('gpt-5')
  )
}

export function isSupportedFlexServiceTier(model: Model): boolean {
  return isSupportFlexServiceTierModel(model)
}

export function isSupportedModel(model: OpenAI.Models.Model): boolean {
  if (!model) {
    return false
  }

  const modelId = getLowerBaseModelName(model.id)

  return !NOT_SUPPORTED_REGEX.test(modelId)
}

/**
 * Check if the model supports temperature parameter
 * @param model - The model to check
 * @returns true if the model supports temperature parameter
 */
export function isSupportTemperatureModel(model: Model | undefined | null, assistant?: Assistant): boolean {
  if (!model) {
    return false
  }

  // OpenAI reasoning models (except open weight) don't support temperature
  if (isOpenAIReasoningModel(model) && !isOpenAIOpenWeightModel(model)) {
    if (isGPT52SeriesModel(model) && assistant?.settings?.reasoning_effort === 'none') {
      return true
    }
    return false
  }

  // OpenAI chat completion only models don't support temperature
  if (isOpenAIChatCompletionOnlyModel(model)) {
    return false
  }

  // Qwen MT models don't support temperature
  if (isQwenMTModel(model)) {
    return false
  }

  return true
}

/**
 * Check if the model supports top_p parameter
 * @param model - The model to check
 * @returns true if the model supports top_p parameter
 */
export function isSupportTopPModel(model: Model | undefined | null, assistant?: Assistant): boolean {
  if (!model) {
    return false
  }

  // OpenAI reasoning models (except open weight) don't support top_p
  if (isOpenAIReasoningModel(model) && !isOpenAIOpenWeightModel(model)) {
    if (isGPT52SeriesModel(model) && assistant?.settings?.reasoning_effort === 'none') {
      return true
    }
    return false
  }

  // OpenAI chat completion only models don't support top_p
  if (isOpenAIChatCompletionOnlyModel(model)) {
    return false
  }

  // Qwen MT models don't support top_p
  if (isQwenMTModel(model)) {
    return false
  }

  return true
}

/**
 * Check if the model enforces mutual exclusivity between temperature and top_p parameters.
 * Currently only Claude 4.5 reasoning models require this constraint.
 * @param model - The model to check
 * @returns true if temperature and top_p are mutually exclusive for this model
 */
export function isTemperatureTopPMutuallyExclusiveModel(model: Model | undefined | null): boolean {
  if (!model) return false
  return isClaude45ReasoningModel(model)
}

export function isGemmaModel(model?: Model): boolean {
  if (!model) {
    return false
  }

  const modelId = getLowerBaseModelName(model.id)
  return modelId.includes('gemma-') || model.group === 'Gemma'
}

export function isZhipuModel(model: Model): boolean {
  const modelId = getLowerBaseModelName(model.id)
  return modelId.includes('glm') || model.provider === SystemProviderIds.zhipu
}

export function isMoonshotModel(model: Model): boolean {
  const modelId = getLowerBaseModelName(model.id)
  return ['moonshot', 'kimi'].some((m) => modelId.includes(m))
}

/**
 * 按 Qwen 系列模型分组
 * @param models 模型列表
 * @returns 分组后的模型
 */
export function groupQwenModels(models: Model[]): Record<string, Model[]> {
  return models.reduce(
    (groups, model) => {
      const modelId = getLowerBaseModelName(model.id)
      // 匹配 Qwen 系列模型的前缀
      const prefixMatch = modelId.match(/^(qwen(?:\d+\.\d+|2(?:\.\d+)?|-\d+b|-(?:max|coder|vl)))/i)
      // 匹配 qwen2.5、qwen2、qwen-7b、qwen-max、qwen-coder 等
      const groupKey = prefixMatch ? prefixMatch[1] : model.group || '其他'

      if (!groups[groupKey]) {
        groups[groupKey] = []
      }
      groups[groupKey].push(model)

      return groups
    },
    {} as Record<string, Model[]>
  )
}

// 模型集合功能测试
export const isVisionModels = (models: Model[]) => {
  return models.every((model) => isVisionModel(model))
}

export const isGenerateImageModels = (models: Model[]) => {
  return models.every((model) => isGenerateImageModel(model))
}

export const isAnthropicModel = (model?: Model): boolean => {
  if (!model) {
    return false
  }

  const modelId = getLowerBaseModelName(model.id)
  return modelId.startsWith('claude')
}

const NOT_SUPPORT_TEXT_DELTA_MODEL_REGEX = new RegExp('qwen-mt-(?:turbo|plus)')

export const isNotSupportTextDeltaModel = (model: Model): boolean => {
  const modelId = getLowerBaseModelName(model.id)
  return NOT_SUPPORT_TEXT_DELTA_MODEL_REGEX.test(modelId)
}

export const isNotSupportSystemMessageModel = (model: Model): boolean => {
  return isQwenMTModel(model) || isGemmaModel(model)
}

// Verbosity settings is only supported by GPT-5 and newer models
// Specifically, GPT-5 and GPT-5.1 for now
// gpt-5-pro only supports 'high', other GPT-5 models support all levels
const MODEL_SUPPORTED_VERBOSITY: readonly {
  readonly validator: (model: Model) => boolean
  readonly values: readonly ValidOpenAIVerbosity[]
}[] = [
  // gpt-5-pro
  { validator: isGPT5ProModel, values: ['high'] },
  // gpt-5 except gpt-5-pro
  {
    validator: (model: Model) => isGPT5SeriesModel(model) && !isGPT5ProModel(model),
    values: ['low', 'medium', 'high']
  },
  // gpt-5.1
  { validator: isGPT51SeriesModel, values: ['low', 'medium', 'high'] }
]

/**
 * Returns the list of supported verbosity levels for the given model.
 * If the model is not recognized as a GPT-5 series model, only `undefined` is returned.
 * For GPT-5-pro, only 'high' is supported; for other GPT-5 models, 'low', 'medium', and 'high' are supported.
 * For GPT-5.1 series models, 'low', 'medium', and 'high' are supported.
 * @param model - The model to check
 * @returns An array of supported verbosity levels, always including `undefined` as the first element and `null` when applicable
 */
export const getModelSupportedVerbosity = (model: Model | undefined | null): OpenAIVerbosity[] => {
  if (!model || !isSupportVerbosityModel(model)) {
    return [undefined]
  }

  let supportedValues: ValidOpenAIVerbosity[] = []

  for (const { validator, values } of MODEL_SUPPORTED_VERBOSITY) {
    if (validator(model)) {
      supportedValues = [null, ...values]
      break
    }
  }

  return [undefined, ...supportedValues]
}

export const isGeminiModel = (model: Model) => {
  const modelId = getLowerBaseModelName(model.id)
  return modelId.includes('gemini')
}

export const isGrokModel = (model: Model) => {
  const modelId = getLowerBaseModelName(model.id)
  return modelId.includes('grok')
}

// zhipu 视觉推理模型用这组 special token 标记推理结果
export const ZHIPU_RESULT_TOKENS = ['<|begin_of_box|>', '<|end_of_box|>'] as const

export const agentModelFilter = (model: Model): boolean => {
  return !isEmbeddingModel(model) && !isRerankModel(model) && !isTextToImageModel(model)
}

export const isMaxTemperatureOneModel = (model: Model): boolean => {
  if (isZhipuModel(model) || isAnthropicModel(model) || isMoonshotModel(model)) {
    return true
  }
  return false
}

export const isGemini3Model = (model: Model) => {
  const modelId = getLowerBaseModelName(model.id)
  return modelId.includes('gemini-3')
}

export const isGemini3ThinkingTokenModel = (model: Model) => {
  const modelId = getLowerBaseModelName(model.id)
  return isGemini3Model(model) && !modelId.includes('image')
}

/**
 * Check if the model is a Gemini 3 Flash model
 * Matches: gemini-3-flash, gemini-3-flash-preview, gemini-3-flash-preview-09-2025, gemini-flash-latest (alias)
 * Excludes: gemini-3-flash-image-preview
 * @param model - The model to check
 * @returns true if the model is a Gemini 3 Flash model
 */
export const isGemini3FlashModel = (model: Model | undefined | null): boolean => {
  if (!model) {
    return false
  }
  const modelId = getLowerBaseModelName(model.id)
  // Check for gemini-flash-latest alias (currently points to gemini-3-flash, may change in future)
  if (modelId === 'gemini-flash-latest') {
    return true
  }
  // Check for gemini-3-flash with optional suffixes, excluding image variants
  return /gemini-3-flash(?!-image)(?:-[\w-]+)*$/i.test(modelId)
}

/**
 * Check if the model is a Gemini 3 Pro model
 * Matches: gemini-3-pro, gemini-3-pro-preview, gemini-3-pro-preview-09-2025, gemini-pro-latest (alias)
 * Excludes: gemini-3-pro-image-preview
 * @param model - The model to check
 * @returns true if the model is a Gemini 3 Pro model
 */
export const isGemini3ProModel = (model: Model | undefined | null): boolean => {
  if (!model) {
    return false
  }
  const modelId = getLowerBaseModelName(model.id)
  // Check for gemini-pro-latest alias (currently points to gemini-3-pro, may change in future)
  if (modelId === 'gemini-pro-latest') {
    return true
  }
  // Check for gemini-3-pro with optional suffixes, excluding image variants
  return /gemini-3-pro(?!-image)(?:-[\w-]+)*$/i.test(modelId)
}
