import { describe, expect, it } from 'vitest'

import {
  ASSISTANT_MODELS,
  DEFAULT_ASSISTANT_ADVANCE_MODEL_ID,
  DEFAULT_ASSISTANT_BASE_MODEL_ID,
  DEFAULT_COMPLETION_MODEL,
  defaultAssistantModelId,
  getAssistantModelEntry,
  getDefaultModelForProvider,
  isAdvanceOnlyModelId,
  isAssistantBaseModelId,
  isKnownAssistantModelId,
  openaiModelEntry,
  PROVIDERS,
} from './model.utils'
import type { ProviderName } from './model.utils'

describe('model.utils', () => {
  describe('getDefaultModelForProvider', () => {
    it('should return correct default for bedrock provider', () => {
      const result = getDefaultModelForProvider('bedrock')
      expect(result).toBe('openai.gpt-oss-120b-1:0')
    })

    it('should return correct default for openai provider', () => {
      const result = getDefaultModelForProvider('openai')
      expect(result).toBe('gpt-5.4-nano')
    })

    it('should return undefined for unknown provider', () => {
      const result = getDefaultModelForProvider('unknown' as ProviderName)
      expect(result).toBeUndefined()
    })
  })

  describe('PROVIDERS registry', () => {
    it('should have bedrock provider with models', () => {
      expect(PROVIDERS.bedrock).toBeDefined()
      expect(PROVIDERS.bedrock.models).toBeDefined()
      expect(Object.keys(PROVIDERS.bedrock.models)).toContain(
        'anthropic.claude-3-7-sonnet-20250219-v1:0'
      )
      expect(Object.keys(PROVIDERS.bedrock.models)).toContain('openai.gpt-oss-120b-1:0')
    })

    it('should have openai provider with models', () => {
      expect(PROVIDERS.openai).toBeDefined()
      expect(PROVIDERS.openai.models).toBeDefined()
      expect(Object.keys(PROVIDERS.openai.models)).toContain('gpt-5.3-codex')
      expect(Object.keys(PROVIDERS.openai.models)).toContain('gpt-5.4-nano')
    })

    it('should have exactly one default model per provider', () => {
      const providers: ProviderName[] = ['bedrock', 'openai']

      providers.forEach((provider) => {
        const models = PROVIDERS[provider].models
        const defaultModels = Object.entries(models).filter(([_, config]) => config.default)
        expect(defaultModels.length).toBe(1)
      })
    })

    it('should have valid model configurations', () => {
      const providers: ProviderName[] = ['bedrock', 'openai']

      providers.forEach((provider) => {
        const models = PROVIDERS[provider].models
        Object.entries(models).forEach(([_modelId, config]) => {
          expect(config).toHaveProperty('default')
          expect(typeof config.default).toBe('boolean')
        })
      })
    })

    it('should have bedrock model with systemProviderOptions', () => {
      const sonnetModel = PROVIDERS.bedrock.models['anthropic.claude-3-7-sonnet-20250219-v1:0']
      expect(sonnetModel.systemProviderOptions).toBeDefined()
      expect(sonnetModel.systemProviderOptions?.bedrock).toBeDefined()
      expect(sonnetModel.systemProviderOptions?.bedrock?.cachePoint).toEqual({
        type: 'default',
      })
    })

    it('should have openai provider with providerOptions', () => {
      expect(PROVIDERS.openai.providerOptions).toBeDefined()
      expect(PROVIDERS.openai.providerOptions?.openai).toBeDefined()
      expect(PROVIDERS.openai.providerOptions?.openai?.reasoningEffort).toBeUndefined()
    })
  })

  describe('assistant model registry', () => {
    it('should have non-empty base and advance tiers', () => {
      expect(
        ASSISTANT_MODELS.filter((m) => !m.requiresAdvanceModelEntitlement).length
      ).toBeGreaterThan(0)
      expect(
        ASSISTANT_MODELS.filter((m) => m.requiresAdvanceModelEntitlement).length
      ).toBeGreaterThan(0)
    })

    it('all model IDs should be unique', () => {
      const ids = ASSISTANT_MODELS.map((m) => m.id)
      expect(new Set(ids).size).toBe(ids.length)
    })

    it('should have all models in openai provider registry', () => {
      ASSISTANT_MODELS.forEach((entry) => {
        expect(Object.keys(PROVIDERS.openai.models)).toContain(entry.id)
      })
    })

    it('defaults should satisfy unions', () => {
      expect(DEFAULT_ASSISTANT_BASE_MODEL_ID).toBe('gpt-5.4-nano')
      expect(DEFAULT_ASSISTANT_ADVANCE_MODEL_ID).toBe('gpt-5.3-codex')
      expect(defaultAssistantModelId(false)).toBe(DEFAULT_ASSISTANT_BASE_MODEL_ID)
      expect(defaultAssistantModelId(true)).toBe(DEFAULT_ASSISTANT_ADVANCE_MODEL_ID)
    })

    it('isAssistantBaseModelId / isAdvanceOnlyModelId', () => {
      expect(isAssistantBaseModelId('gpt-5.4-nano')).toBe(true)
      expect(isAssistantBaseModelId('gpt-5.3-codex')).toBe(false)
      expect(isAdvanceOnlyModelId('gpt-5.3-codex')).toBe(true)
      expect(isAdvanceOnlyModelId('gpt-5.4-nano')).toBe(false)
    })

    it('isKnownAssistantModelId', () => {
      expect(isKnownAssistantModelId('gpt-5.4-nano')).toBe(true)
      expect(isKnownAssistantModelId('gpt-5.3-codex')).toBe(true)
      expect(isKnownAssistantModelId('gpt-5')).toBe(false)
      expect(isKnownAssistantModelId('gpt-5-mini')).toBe(false)
      expect(isKnownAssistantModelId('unknown')).toBe(false)
    })

    it('getAssistantModelEntry returns config for known ids', () => {
      expect(getAssistantModelEntry('gpt-5.4-nano').reasoningEffort).toBe('low')
      expect(getAssistantModelEntry('gpt-5.3-codex').reasoningEffort).toBe('low')
      expect(getAssistantModelEntry('gpt-5.4-nano')).toEqual(
        ASSISTANT_MODELS.find((m) => m.id === 'gpt-5.4-nano')
      )
    })

    it('DEFAULT_COMPLETION_MODEL is gpt-5.4-nano with no reasoning effort', () => {
      expect(DEFAULT_COMPLETION_MODEL.id).toBe(DEFAULT_ASSISTANT_BASE_MODEL_ID)
      expect(DEFAULT_COMPLETION_MODEL.reasoningEffort).toBe('none')
    })

    it('openaiModelEntry enforces valid reasoning effort at compile time', () => {
      // Valid: supported effort level
      const withEffort = openaiModelEntry({
        id: 'gpt-5.4-nano',
        reasoningEffort: 'low',
      })
      expect(withEffort.reasoningEffort).toBe('low')

      // Valid: no effort
      const withoutEffort = openaiModelEntry({ id: 'gpt-5.4-nano' })
      expect(withoutEffort.reasoningEffort).toBeUndefined()
    })
  })
})
