import { isEmbeddingModel, isRerankModel } from '@renderer/config/models/embedding'
import type { Model } from '@renderer/types'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import { isOpenAIReasoningModel } from '../openai'
import { isQwenMTModel } from '../qwen'
import {
  agentModelFilter,
  getModelSupportedVerbosity,
  groupQwenModels,
  isAnthropicModel,
  isClaude46SeriesModel,
  isClaude47SeriesModel,
  isDeepSeekModel,
  isGemini3FlashModel,
  isGemini3ProModel,
  isGemini31ProModel,
  isGeminiModel,
  isGemmaModel,
  isGenerateImageModels,
  isMaxTemperatureOneModel,
  isNotSupportSystemMessageModel,
  isNotSupportTextDeltaModel,
  isSupportedFlexServiceTier,
  isSupportedModel,
  isSupportFlexServiceTierModel,
  isSupportTemperatureModel,
  isSupportTopPModel,
  isTemperatureTopPMutuallyExclusiveModel,
  isVisionModels,
  isZhipuModel
} from '../utils'
import { isGenerateImageModel, isTextToImageModel, isVisionModel } from '../vision'
import { isOpenAIWebSearchChatCompletionOnlyModel } from '../websearch'

vi.mock('@renderer/hooks/useStore', () => ({
  getStoreProviders: vi.fn(() => [])
}))

vi.mock('@renderer/store', () => ({
  __esModule: true,
  default: {
    getState: () => ({
      llm: { providers: [] },
      settings: {}
    })
  },
  useAppDispatch: vi.fn(),
  useAppSelector: vi.fn()
}))

vi.mock('@renderer/store/settings', () => {
  const noop = vi.fn()
  return new Proxy(
    {},
    {
      get: (_target, prop) => {
        if (prop === 'initialState') {
          return {}
        }
        return noop
      }
    }
  )
})

vi.mock('@renderer/hooks/useSettings', () => ({
  useSettings: vi.fn(() => ({})),
  useNavbarPosition: vi.fn(() => ({ navbarPosition: 'left' })),
  useMessageStyle: vi.fn(() => ({ isBubbleStyle: false })),
  getStoreSetting: vi.fn()
}))

vi.mock('@renderer/config/models/embedding', () => ({
  isEmbeddingModel: vi.fn(),
  isRerankModel: vi.fn()
}))

vi.mock('../vision', () => ({
  isGenerateImageModel: vi.fn(),
  isTextToImageModel: vi.fn(),
  isVisionModel: vi.fn()
}))

vi.mock(import('../openai'), async (importOriginal) => {
  const actual = await importOriginal()
  return {
    ...actual,
    isOpenAIReasoningModel: vi.fn()
  }
})

vi.mock('../websearch', () => ({
  isOpenAIWebSearchChatCompletionOnlyModel: vi.fn()
}))

const createModel = (overrides: Partial<Model> = {}): Model => ({
  id: 'gpt-4o',
  name: 'gpt-4o',
  provider: 'openai',
  group: 'OpenAI',
  ...overrides
})

const embeddingMock = vi.mocked(isEmbeddingModel)
const rerankMock = vi.mocked(isRerankModel)
const visionMock = vi.mocked(isVisionModel)
const textToImageMock = vi.mocked(isTextToImageModel)
const generateImageMock = vi.mocked(isGenerateImageModel)
const reasoningMock = vi.mocked(isOpenAIReasoningModel)
const openAIWebSearchOnlyMock = vi.mocked(isOpenAIWebSearchChatCompletionOnlyModel)

describe('model utils', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    embeddingMock.mockReturnValue(false)
    rerankMock.mockReturnValue(false)
    visionMock.mockReturnValue(true)
    textToImageMock.mockReturnValue(false)
    generateImageMock.mockReturnValue(true)
    reasoningMock.mockReturnValue(false)
    openAIWebSearchOnlyMock.mockReturnValue(false)
  })

  describe('Verbosity support', () => {
    describe('getModelSupportedVerbosity', () => {
      const allLevels = [undefined, null, 'low', 'medium', 'high']
      const mediumOnly = [undefined, null, 'medium']

      it('GPT-5 models', () => {
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5-mini' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5-nano' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5-pro' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5-chat' }))).toEqual(mediumOnly)
      })

      it('GPT-5.1 models', () => {
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.1' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.1-chat-latest' }))).toEqual(mediumOnly)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.1-codex' }))).toEqual(mediumOnly)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.1-codex-mini' }))).toEqual(mediumOnly)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.1-codex-max' }))).toEqual(mediumOnly)
      })

      it('GPT-5.2 models', () => {
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.2' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.2-pro' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.2-codex' }))).toEqual(mediumOnly)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.2-chat-latest' }))).toEqual(mediumOnly)
      })

      it('GPT-5.3 models', () => {
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.3-codex' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.3-chat-latest' }))).toEqual(mediumOnly)
      })

      it('GPT-5.4 models', () => {
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.4' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.4-pro' }))).toEqual(allLevels)
      })

      it('future GPT-5.5+ models fallback correctly', () => {
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.5' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.5-pro' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.5-codex' }))).toEqual(allLevels)
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5.5-chat-latest' }))).toEqual(mediumOnly)
      })

      it('returns only undefined for non-GPT-5 models', () => {
        expect(getModelSupportedVerbosity(createModel({ id: 'gpt-4o' }))).toEqual([undefined])
        expect(getModelSupportedVerbosity(createModel({ id: 'claude-3.5' }))).toEqual([undefined])
      })

      it('returns only undefined for undefiend/null input', () => {
        expect(getModelSupportedVerbosity(undefined)).toEqual([undefined])
        expect(getModelSupportedVerbosity(null)).toEqual([undefined])
      })
    })
  })

  describe('Flex service tier support', () => {
    describe('isSupportFlexServiceTierModel', () => {
      it('returns true for supported models', () => {
        expect(isSupportFlexServiceTierModel(createModel({ id: 'o3' }))).toBe(true)
        expect(isSupportFlexServiceTierModel(createModel({ id: 'o4-mini' }))).toBe(true)
        expect(isSupportFlexServiceTierModel(createModel({ id: 'gpt-5-preview' }))).toBe(true)
      })

      it('returns false for unsupported models', () => {
        expect(isSupportFlexServiceTierModel(createModel({ id: 'o3-mini' }))).toBe(false)
      })
    })

    describe('isSupportedFlexServiceTier', () => {
      it('returns false for non-flex models', () => {
        expect(isSupportedFlexServiceTier(createModel({ id: 'gpt-4o' }))).toBe(false)
      })
    })
  })

  describe('Temperature and top-p support', () => {
    describe('isSupportTemperatureModel', () => {
      it('returns false for reasoning models (non-open weight)', () => {
        const model = createModel({ id: 'o1' })
        reasoningMock.mockReturnValue(true)
        expect(isSupportTemperatureModel(model)).toBe(false)
      })

      it('returns true for open weight models', () => {
        const openWeight = createModel({ id: 'gpt-oss-debug' })
        expect(isSupportTemperatureModel(openWeight)).toBe(true)
      })

      it('returns false for chat-only models', () => {
        const chatOnly = createModel({ id: 'o1-preview' })
        expect(isSupportTemperatureModel(chatOnly)).toBe(false)
      })

      it('returns false for Qwen MT models', () => {
        const qwenMt = createModel({ id: 'qwen-mt-large', provider: 'aliyun' })
        expect(isSupportTemperatureModel(qwenMt)).toBe(false)
      })

      it('returns false for Kimi K2.5+ and K3+ models', () => {
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k2.5' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'Kimi-K2.5' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'moonshot/kimi-k2.5' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k2.6' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'Kimi-K2.6' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'moonshot/kimi-k2.6' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k2.7' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k3' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k3.5' }))).toBe(false)
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k4' }))).toBe(false)
      })

      it('returns true for older Kimi models', () => {
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k2' }))).toBe(true)
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k2-thinking' }))).toBe(true)
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k2-0711-preview' }))).toBe(true)
        expect(isSupportTemperatureModel(createModel({ id: 'kimi-k2-turbo-preview' }))).toBe(true)
      })

      it('returns false for null/undefined models', () => {
        expect(isSupportTemperatureModel(null)).toBe(false)
        expect(isSupportTemperatureModel(undefined)).toBe(false)
      })

      it('returns true for regular GPT models', () => {
        const model = createModel({ id: 'gpt-4' })
        expect(isSupportTemperatureModel(model)).toBe(true)
      })
    })

    describe('isSupportTopPModel', () => {
      it('returns false for reasoning models (non-open weight)', () => {
        const model = createModel({ id: 'o1' })
        reasoningMock.mockReturnValue(true)
        expect(isSupportTopPModel(model)).toBe(false)
      })

      it('returns true for open weight models', () => {
        const openWeight = createModel({ id: 'gpt-oss-debug' })
        expect(isSupportTopPModel(openWeight)).toBe(true)
      })

      it('returns false for chat-only models', () => {
        const chatOnly = createModel({ id: 'o1-preview' })
        expect(isSupportTopPModel(chatOnly)).toBe(false)
      })

      it('returns false for Qwen MT models', () => {
        const qwenMt = createModel({ id: 'qwen-mt-large', provider: 'aliyun' })
        expect(isSupportTopPModel(qwenMt)).toBe(false)
      })

      it('returns false for Kimi K2.5+ and K3+ models', () => {
        expect(isSupportTopPModel(createModel({ id: 'kimi-k2.5' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'Kimi-K2.5' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'moonshot/kimi-k2.5' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'kimi-k2.6' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'Kimi-K2.6' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'moonshot/kimi-k2.6' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'kimi-k2.7' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'kimi-k3' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'kimi-k3.5' }))).toBe(false)
        expect(isSupportTopPModel(createModel({ id: 'kimi-k4' }))).toBe(false)
      })

      it('returns true for older Kimi models', () => {
        expect(isSupportTopPModel(createModel({ id: 'kimi-k2' }))).toBe(true)
        expect(isSupportTopPModel(createModel({ id: 'kimi-k2-thinking' }))).toBe(true)
        expect(isSupportTopPModel(createModel({ id: 'kimi-k2-0711-preview' }))).toBe(true)
        expect(isSupportTopPModel(createModel({ id: 'kimi-k2-turbo-preview' }))).toBe(true)
      })

      it('returns false for null/undefined models', () => {
        expect(isSupportTopPModel(null)).toBe(false)
        expect(isSupportTopPModel(undefined)).toBe(false)
      })

      it('returns true for regular GPT models', () => {
        const model = createModel({ id: 'gpt-4' })
        expect(isSupportTopPModel(model)).toBe(true)
      })
    })

    describe('isTemperatureTopPMutuallyExclusiveModel', () => {
      it('returns true for Claude 4.5 reasoning models', () => {
        const claude45Sonnet = createModel({ id: 'claude-sonnet-4.5-20250514' })
        expect(isTemperatureTopPMutuallyExclusiveModel(claude45Sonnet)).toBe(true)

        const claude45Opus = createModel({ id: 'claude-opus-4.5-20250514' })
        expect(isTemperatureTopPMutuallyExclusiveModel(claude45Opus)).toBe(true)
      })

      it('returns false for Claude 4 models', () => {
        const claude4Sonnet = createModel({ id: 'claude-sonnet-4-20250514' })
        expect(isTemperatureTopPMutuallyExclusiveModel(claude4Sonnet)).toBe(false)
      })

      it('returns false for Claude 3.x models', () => {
        const claude35Sonnet = createModel({ id: 'claude-3-5-sonnet-20241022' })
        expect(isTemperatureTopPMutuallyExclusiveModel(claude35Sonnet)).toBe(false)

        const claude3Opus = createModel({ id: 'claude-3-opus-20240229' })
        expect(isTemperatureTopPMutuallyExclusiveModel(claude3Opus)).toBe(false)
      })

      it('returns false for other AI models', () => {
        expect(isTemperatureTopPMutuallyExclusiveModel(createModel({ id: 'gpt-4o' }))).toBe(false)
        expect(isTemperatureTopPMutuallyExclusiveModel(createModel({ id: 'o1' }))).toBe(false)
        expect(isTemperatureTopPMutuallyExclusiveModel(createModel({ id: 'gemini-2.0-flash' }))).toBe(false)
        expect(isTemperatureTopPMutuallyExclusiveModel(createModel({ id: 'qwen-max' }))).toBe(false)
      })

      it('returns false for null/undefined models', () => {
        expect(isTemperatureTopPMutuallyExclusiveModel(null)).toBe(false)
        expect(isTemperatureTopPMutuallyExclusiveModel(undefined)).toBe(false)
      })
    })
  })

  describe('Text delta support', () => {
    describe('isNotSupportTextDeltaModel', () => {
      it('returns true for qwen-mt-turbo and qwen-mt-plus models', () => {
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-turbo' }))).toBe(true)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-plus' }))).toBe(true)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'Qwen-MT-Turbo' }))).toBe(true)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'QWEN-MT-PLUS' }))).toBe(true)
      })

      it('returns false for qwen-mt-flash and other models', () => {
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-flash' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'Qwen-MT-Flash' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-turbo' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-plus' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-max' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen2.5-72b' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-vl-plus' }))).toBe(false)
      })

      it('returns false for non-qwen models', () => {
        expect(isNotSupportTextDeltaModel(createModel({ id: 'gpt-4o' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'claude-3.5' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'glm-4-plus' }))).toBe(false)
      })

      it('handles models with version suffixes', () => {
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-turbo-1201' }))).toBe(true)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-plus-0828' }))).toBe(true)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-turbo-0828' }))).toBe(false)
        expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-plus-latest' }))).toBe(false)
      })
    })
  })

  describe('Model provider detection', () => {
    describe('isGemmaModel', () => {
      it('detects Gemma models by ID', () => {
        expect(isGemmaModel(createModel({ id: 'Gemma-3-27B' }))).toBe(true)
      })

      it('detects Gemma models by group', () => {
        expect(isGemmaModel(createModel({ group: 'Gemma' }))).toBe(true)
      })

      it('returns false for non-Gemma models', () => {
        expect(isGemmaModel(createModel({ id: 'gpt-4o' }))).toBe(false)
      })
    })

    describe('isGeminiModel', () => {
      it('detects Gemini models', () => {
        expect(isGeminiModel(createModel({ id: 'Gemini-2.0' }))).toBe(true)
      })
    })

    describe('isGemini3FlashModel', () => {
      it('detects gemini-3-flash model', () => {
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-flash' }))).toBe(true)
      })

      it('detects gemini-3-flash-preview model', () => {
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-flash-preview' }))).toBe(true)
      })

      it('detects gemini-3-flash with version suffixes', () => {
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-flash-latest' }))).toBe(true)
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-flash-preview-09-2025' }))).toBe(true)
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-flash-exp-1234' }))).toBe(true)
      })

      it('detects gemini-flash-latest alias', () => {
        expect(isGemini3FlashModel(createModel({ id: 'gemini-flash-latest' }))).toBe(true)
        expect(isGemini3FlashModel(createModel({ id: 'Gemini-Flash-Latest' }))).toBe(true)
      })

      it('detects gemini-3-flash with uppercase', () => {
        expect(isGemini3FlashModel(createModel({ id: 'Gemini-3-Flash' }))).toBe(true)
        expect(isGemini3FlashModel(createModel({ id: 'GEMINI-3-FLASH-PREVIEW' }))).toBe(true)
      })

      it('excludes gemini-3-flash-image models', () => {
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-flash-image-preview' }))).toBe(false)
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-flash-image' }))).toBe(false)
      })

      it('returns false for non-flash gemini-3 models', () => {
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-pro' }))).toBe(false)
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-pro-preview' }))).toBe(false)
        expect(isGemini3FlashModel(createModel({ id: 'gemini-3-pro-image-preview' }))).toBe(false)
      })

      it('returns false for other gemini models', () => {
        expect(isGemini3FlashModel(createModel({ id: 'gemini-2-flash' }))).toBe(false)
        expect(isGemini3FlashModel(createModel({ id: 'gemini-2-flash-preview' }))).toBe(false)
        expect(isGemini3FlashModel(createModel({ id: 'gemini-2.5-flash-preview-09-2025' }))).toBe(false)
      })

      it('returns false for null/undefined models', () => {
        expect(isGemini3FlashModel(null)).toBe(false)
        expect(isGemini3FlashModel(undefined)).toBe(false)
      })
    })

    describe('isGemini3ProModel', () => {
      it('detects gemini-3-pro model', () => {
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-pro' }))).toBe(true)
      })

      it('detects gemini-3-pro-preview model', () => {
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-pro-preview' }))).toBe(true)
      })

      it('detects gemini-3-pro with version suffixes', () => {
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-pro-latest' }))).toBe(true)
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-pro-preview-09-2025' }))).toBe(true)
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-pro-exp-1234' }))).toBe(true)
      })

      it('detects gemini-3-pro with uppercase', () => {
        expect(isGemini3ProModel(createModel({ id: 'Gemini-3-Pro' }))).toBe(true)
        expect(isGemini3ProModel(createModel({ id: 'GEMINI-3-PRO-PREVIEW' }))).toBe(true)
      })

      it('excludes gemini-3-pro-image models', () => {
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-pro-image-preview' }))).toBe(false)
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-pro-image' }))).toBe(false)
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-pro-image-latest' }))).toBe(false)
      })

      it('returns false for non-pro gemini-3 models', () => {
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-flash' }))).toBe(false)
        expect(isGemini3ProModel(createModel({ id: 'gemini-3-flash-preview' }))).toBe(false)
      })

      it('returns false for other gemini models', () => {
        expect(isGemini3ProModel(createModel({ id: 'gemini-2-pro' }))).toBe(false)
        expect(isGemini3ProModel(createModel({ id: 'gemini-2.5-pro-preview-09-2025' }))).toBe(false)
      })

      it('returns false for null/undefined models', () => {
        expect(isGemini3ProModel(null)).toBe(false)
        expect(isGemini3ProModel(undefined)).toBe(false)
      })
    })

    describe('isGemini31ProModel', () => {
      it('detects gemini-3.1-pro model', () => {
        expect(isGemini31ProModel(createModel({ id: 'gemini-3.1-pro' }))).toBe(true)
      })

      it('detects gemini-3.1-pro-preview model', () => {
        expect(isGemini31ProModel(createModel({ id: 'gemini-3.1-pro-preview' }))).toBe(true)
      })

      it('detects gemini-3.1-pro with version suffixes', () => {
        expect(isGemini31ProModel(createModel({ id: 'gemini-3.1-pro-latest' }))).toBe(true)
        expect(isGemini31ProModel(createModel({ id: 'gemini-3.1-pro-preview-09-2025' }))).toBe(true)
        expect(isGemini31ProModel(createModel({ id: 'gemini-3.1-pro-exp-1234' }))).toBe(true)
      })

      it('detects gemini-pro-latest alias', () => {
        expect(isGemini31ProModel(createModel({ id: 'gemini-pro-latest' }))).toBe(true)
        expect(isGemini31ProModel(createModel({ id: 'Gemini-Pro-Latest' }))).toBe(true)
      })

      it('detects gemini-3.1-pro with uppercase', () => {
        expect(isGemini31ProModel(createModel({ id: 'Gemini-3.1-Pro' }))).toBe(true)
        expect(isGemini31ProModel(createModel({ id: 'GEMINI-3.1-PRO-PREVIEW' }))).toBe(true)
      })

      it('excludes gemini-3.1-pro-image models', () => {
        expect(isGemini31ProModel(createModel({ id: 'gemini-3.1-pro-image-preview' }))).toBe(false)
        expect(isGemini31ProModel(createModel({ id: 'gemini-3.1-pro-image' }))).toBe(false)
        expect(isGemini31ProModel(createModel({ id: 'gemini-3.1-pro-image-latest' }))).toBe(false)
      })

      it('returns false for non-3.1 gemini-3-pro models', () => {
        expect(isGemini31ProModel(createModel({ id: 'gemini-3-pro' }))).toBe(false)
        expect(isGemini31ProModel(createModel({ id: 'gemini-3-pro-preview' }))).toBe(false)
        expect(isGemini31ProModel(createModel({ id: 'gemini-3-pro-latest' }))).toBe(false)
      })

      it('returns false for other gemini models', () => {
        expect(isGemini31ProModel(createModel({ id: 'gemini-2-pro' }))).toBe(false)
        expect(isGemini31ProModel(createModel({ id: 'gemini-2.5-pro-preview-09-2025' }))).toBe(false)
        expect(isGemini31ProModel(createModel({ id: 'gemini-3-flash' }))).toBe(false)
        expect(isGemini31ProModel(createModel({ id: 'gemini-3-flash-preview' }))).toBe(false)
      })

      it('returns false for null/undefined models', () => {
        expect(isGemini31ProModel(null)).toBe(false)
        expect(isGemini31ProModel(undefined)).toBe(false)
      })
    })

    describe('isZhipuModel', () => {
      it('detects Zhipu models by provider', () => {
        expect(isZhipuModel(createModel({ provider: 'zhipu' }))).toBe(true)
      })

      it('returns false for non-Zhipu models', () => {
        expect(isZhipuModel(createModel({ provider: 'openai' }))).toBe(false)
      })
    })

    describe('isAnthropicModel', () => {
      it('detects Anthropic models', () => {
        expect(isAnthropicModel(createModel({ id: 'claude-3.5' }))).toBe(true)
      })
    })

    describe('isDeepSeekModel', () => {
      it('detects DeepSeek models by id', () => {
        expect(isDeepSeekModel(createModel({ id: 'deepseek-chat' }))).toBe(true)
        expect(isDeepSeekModel(createModel({ id: 'DeepSeek-V3' }))).toBe(true)
      })

      it('detects DeepSeek models by name', () => {
        expect(isDeepSeekModel(createModel({ id: 'custom-id', name: 'DeepSeek V3' }))).toBe(true)
      })

      it('returns false for non-DeepSeek models', () => {
        expect(isDeepSeekModel(createModel({ id: 'gpt-4o' }))).toBe(false)
      })

      it('returns false for missing model', () => {
        expect(isDeepSeekModel(undefined)).toBe(false)
      })
    })

    describe('isQwenMTModel', () => {
      it('detects Qwen MT models', () => {
        expect(isQwenMTModel(createModel({ id: 'qwen-mt-plus' }))).toBe(true)
      })
    })
  })

  describe('System message support', () => {
    describe('isNotSupportSystemMessageModel', () => {
      it('returns true for models that do not support system messages', () => {
        expect(isNotSupportSystemMessageModel(createModel({ id: 'gemma-moe' }))).toBe(true)
      })
    })
  })

  describe('Model grouping', () => {
    describe('groupQwenModels', () => {
      it('groups qwen models by prefix', () => {
        const qwen = createModel({ id: 'Qwen-7B', provider: 'qwen', name: 'Qwen-7B' })
        const qwenOmni = createModel({ id: 'qwen2.5-omni', name: 'qwen2.5-omni' })
        const other = createModel({ id: 'deepseek-v3', group: 'DeepSeek' })

        const grouped = groupQwenModels([qwen, qwenOmni, other])
        expect(Object.keys(grouped)).toContain('qwen-7b')
        expect(Object.keys(grouped)).toContain('qwen2.5')
        expect(grouped.DeepSeek).toContain(other)
      })
    })
  })

  describe('Vision and image generation', () => {
    describe('isVisionModels', () => {
      it('returns true when all models support vision', () => {
        const models = [createModel({ id: 'gpt-4o' }), createModel({ id: 'gpt-4o-mini' })]
        expect(isVisionModels(models)).toBe(true)
      })

      it('returns false when some models do not support vision', () => {
        const models = [createModel({ id: 'gpt-4o' }), createModel({ id: 'gpt-4o-mini' })]
        visionMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
        expect(isVisionModels(models)).toBe(false)
      })
    })

    describe('isGenerateImageModels', () => {
      it('returns true when all models support image generation', () => {
        const models = [createModel({ id: 'gpt-4o' }), createModel({ id: 'gpt-4o-mini' })]
        expect(isGenerateImageModels(models)).toBe(true)
      })

      it('returns false when some models do not support image generation', () => {
        const models = [createModel({ id: 'gpt-4o' }), createModel({ id: 'gpt-4o-mini' })]
        generateImageMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
        expect(isGenerateImageModels(models)).toBe(false)
      })
    })
  })

  describe('Model filtering', () => {
    describe('isSupportedModel', () => {
      it('filters supported OpenAI catalog entries', () => {
        expect(isSupportedModel({ id: 'gpt-4', object: 'model' } as any)).toBe(true)
      })

      it('filters unsupported OpenAI catalog entries', () => {
        expect(isSupportedModel({ id: 'tts-1', object: 'model' } as any)).toBe(false)
      })
    })

    describe('agentModelFilter', () => {
      it('returns true for regular models', () => {
        expect(agentModelFilter(createModel())).toBe(true)
      })

      it('filters out embedding models', () => {
        embeddingMock.mockReturnValueOnce(true)
        expect(agentModelFilter(createModel({ id: 'text-embedding' }))).toBe(false)
      })

      it('filters out rerank models', () => {
        embeddingMock.mockReturnValue(false)
        rerankMock.mockReturnValueOnce(true)
        expect(agentModelFilter(createModel({ id: 'rerank' }))).toBe(false)
      })

      it('filters out text-to-image models', () => {
        rerankMock.mockReturnValue(false)
        textToImageMock.mockReturnValueOnce(true)
        expect(agentModelFilter(createModel({ id: 'gpt-image-1' }))).toBe(false)
      })
    })
  })

  describe('Temperature limits', () => {
    describe('isMaxTemperatureOneModel', () => {
      it('returns true for Zhipu models', () => {
        expect(isMaxTemperatureOneModel(createModel({ id: 'glm-4' }))).toBe(true)
        expect(isMaxTemperatureOneModel(createModel({ id: 'GLM-4-Plus' }))).toBe(true)
        expect(isMaxTemperatureOneModel(createModel({ id: 'glm-3-turbo' }))).toBe(true)
      })

      it('returns true for Anthropic models', () => {
        expect(isMaxTemperatureOneModel(createModel({ id: 'claude-3.5-sonnet' }))).toBe(true)
        expect(isMaxTemperatureOneModel(createModel({ id: 'Claude-3-opus' }))).toBe(true)
        expect(isMaxTemperatureOneModel(createModel({ id: 'claude-2.1' }))).toBe(true)
      })

      it('returns true for Moonshot models', () => {
        expect(isMaxTemperatureOneModel(createModel({ id: 'moonshot-1.0' }))).toBe(true)
        expect(isMaxTemperatureOneModel(createModel({ id: 'kimi-k2-thinking' }))).toBe(true)
        expect(isMaxTemperatureOneModel(createModel({ id: 'Moonshot-Pro' }))).toBe(true)
      })

      it('returns false for other models', () => {
        expect(isMaxTemperatureOneModel(createModel({ id: 'gpt-4o' }))).toBe(false)
        expect(isMaxTemperatureOneModel(createModel({ id: 'gpt-4-turbo' }))).toBe(false)
        expect(isMaxTemperatureOneModel(createModel({ id: 'qwen-max' }))).toBe(false)
        expect(isMaxTemperatureOneModel(createModel({ id: 'gemini-pro' }))).toBe(false)
      })
    })
  })

  describe('Claude 4.6 Models Detection', () => {
    describe('isClaude46SeriesModel', () => {
      it('detects Opus 4.6 in direct API format', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4-6' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4.6' }))).toBe(true)
      })

      it('detects Opus 4.6 with version suffixes', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4-6-20251201' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4.6-20251201' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4-6-preview' }))).toBe(true)
      })

      it('detects Opus 4.6 in AWS Bedrock format', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'anthropic.claude-opus-4-6-v1' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'anthropic.claude-opus-4-6-v2:0' }))).toBe(true)
      })

      it('detects Opus 4.6 with provider prefix', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'anthropic/claude-opus-4-6' }))).toBe(true)
      })

      it('handles case insensitivity', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'CLAUDE-OPUS-4-6' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'Claude-Opus-4.6' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'Anthropic.Claude-Opus-4-6-V1' }))).toBe(true)
      })

      it('returns false for other Claude models', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4-5' }))).toBe(false)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4.5' }))).toBe(false)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4' }))).toBe(false)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4-0' }))).toBe(false)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-opus-4-1' }))).toBe(false)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-3-opus' }))).toBe(false)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-3.5-sonnet' }))).toBe(false)
      })

      it('detects Sonnet 4.6 in direct API format', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'claude-sonnet-4-6' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-sonnet-4.6' }))).toBe(true)
      })

      it('detects Sonnet 4.6 with version suffixes', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'claude-sonnet-4-6-20251201' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-sonnet-4.6-20251201' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'claude-sonnet-4-6-preview' }))).toBe(true)
      })

      it('detects Sonnet 4.6 in AWS Bedrock format', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'anthropic.claude-sonnet-4-6' }))).toBe(true)
      })

      it('detects Sonnet 4.6 with provider prefix', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'anthropic/claude-sonnet-4-6' }))).toBe(true)
      })

      it('handles case insensitivity for Sonnet', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'CLAUDE-SONNET-4-6' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'Claude-Sonnet-4.6' }))).toBe(true)
        expect(isClaude46SeriesModel(createModel({ id: 'Anthropic.Claude-Sonnet-4-6-V1' }))).toBe(true)
      })

      it('returns false for non-Claude models', () => {
        expect(isClaude46SeriesModel(createModel({ id: 'gpt-4o' }))).toBe(false)
        expect(isClaude46SeriesModel(createModel({ id: 'gemini-pro' }))).toBe(false)
        expect(isClaude46SeriesModel(createModel({ id: 'qwen-max' }))).toBe(false)
      })

      it('returns false for undefined and null', () => {
        expect(isClaude46SeriesModel(undefined as unknown as Model)).toBe(false)
        expect(isClaude46SeriesModel(null as unknown as Model)).toBe(false)
      })
    })
  })

  describe('Claude 4.7 Models Detection', () => {
    describe('isClaude47SeriesModel', () => {
      it('detects Opus 4.7 in direct API format', () => {
        expect(isClaude47SeriesModel(createModel({ id: 'claude-opus-4-7' }))).toBe(true)
        expect(isClaude47SeriesModel(createModel({ id: 'claude-opus-4.7' }))).toBe(true)
      })

      it('detects Opus 4.7 with version suffixes', () => {
        expect(isClaude47SeriesModel(createModel({ id: 'claude-opus-4-7-20260401' }))).toBe(true)
        expect(isClaude47SeriesModel(createModel({ id: 'claude-opus-4-7-preview' }))).toBe(true)
      })

      it('detects Opus 4.7 in AWS Bedrock format', () => {
        expect(isClaude47SeriesModel(createModel({ id: 'anthropic.claude-opus-4-7-v1' }))).toBe(true)
        expect(isClaude47SeriesModel(createModel({ id: 'anthropic.claude-opus-4-7-v2:0' }))).toBe(true)
      })

      it('detects Opus 4.7 with provider prefix', () => {
        expect(isClaude47SeriesModel(createModel({ id: 'anthropic/claude-opus-4-7' }))).toBe(true)
      })

      it('handles case insensitivity', () => {
        expect(isClaude47SeriesModel(createModel({ id: 'CLAUDE-OPUS-4-7' }))).toBe(true)
        expect(isClaude47SeriesModel(createModel({ id: 'Claude-Opus-4.7' }))).toBe(true)
      })

      it('returns false for other Claude models', () => {
        expect(isClaude47SeriesModel(createModel({ id: 'claude-opus-4-6' }))).toBe(false)
        expect(isClaude47SeriesModel(createModel({ id: 'claude-opus-4-5' }))).toBe(false)
        expect(isClaude47SeriesModel(createModel({ id: 'claude-sonnet-4-7' }))).toBe(false)
        expect(isClaude47SeriesModel(createModel({ id: 'claude-haiku-4-7' }))).toBe(false)
      })

      it('returns false for undefined and null', () => {
        expect(isClaude47SeriesModel(undefined as unknown as Model)).toBe(false)
        expect(isClaude47SeriesModel(null as unknown as Model)).toBe(false)
      })
    })
  })

  describe('Gemma 4 Model Detection', () => {
    describe('isGemmaModel', () => {
      it('detects Gemma 4 GenAI format', () => {
        expect(isGemmaModel(createModel({ id: 'gemma-4-e2b' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma-4-e4b' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma-4-26b-moe' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma-4-31b' }))).toBe(true)
      })

      it('detects Gemma 4 Ollama format', () => {
        expect(isGemmaModel(createModel({ id: 'gemma4' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma4:e2b' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma4:e4b' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma4:26b' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma4:31b' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma4:latest' }))).toBe(true)
      })

      it('detects Gemma 4 with provider prefix', () => {
        expect(isGemmaModel(createModel({ id: 'google/gemma-4-31b' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'openrouter/gemma-4-31b' }))).toBe(true)
      })

      it('still detects earlier Gemma versions', () => {
        expect(isGemmaModel(createModel({ id: 'gemma-2b' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma-2-27b-it' }))).toBe(true)
        expect(isGemmaModel(createModel({ id: 'gemma-3-27b' }))).toBe(true)
      })
    })
  })
})
