import { dataApiService } from '@data/DataApiService'
import { MODEL_CAPABILITY } from '@shared/data/types/model'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import { buildModelListSyncPreview } from '../buildModelListSyncPreview'
import { fetchResolvedProviderModels } from '../modelSync'

vi.mock('@data/DataApiService', () => ({
  dataApiService: {
    get: vi.fn()
  }
}))

vi.mock('../modelSync', () => ({
  fetchResolvedProviderModels: vi.fn()
}))

describe('buildModelListSyncPreview', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('uses API-provided presetModelId to include only preset-owned missing models', async () => {
    vi.mocked(dataApiService.get).mockResolvedValue([
      {
        id: 'openai::gpt-4o',
        providerId: 'openai',
        apiModelId: 'gpt-4o',
        presetModelId: 'gpt-4o',
        name: 'GPT-4o',
        capabilities: [MODEL_CAPABILITY.FUNCTION_CALL],
        supportsStreaming: true,
        isEnabled: true,
        isHidden: false
      },
      {
        id: 'openai::custom-model',
        providerId: 'openai',
        apiModelId: 'custom-model',
        presetModelId: null,
        name: 'Custom Model',
        capabilities: [],
        supportsStreaming: true,
        isEnabled: true,
        isHidden: false
      }
    ])
    vi.mocked(fetchResolvedProviderModels).mockResolvedValue([
      {
        id: 'openai::gpt-5',
        providerId: 'openai',
        apiModelId: 'gpt-5',
        name: 'GPT-5',
        capabilities: [MODEL_CAPABILITY.FUNCTION_CALL],
        supportsStreaming: true,
        isEnabled: true,
        isHidden: false
      }
    ])

    const preview = await buildModelListSyncPreview({
      providerId: 'openai'
    })

    expect(dataApiService.get).toHaveBeenCalledWith('/models', { query: { providerId: 'openai' } })
    expect(preview.added.map((model) => model.id)).toEqual(['openai::gpt-5'])
    expect(preview.missing.map((item) => item.model.id)).toEqual(['openai::gpt-4o'])
    expect(preview.missing.map((item) => item.removalReason)).toEqual(['missing_from_provider'])
    expect(preview).not.toHaveProperty('referenceSummary')
    expect(preview).not.toHaveProperty('replacementSuggestions')
  })

  it('does not mark registry-available presets as missing when upstream models omit them', async () => {
    vi.mocked(dataApiService.get).mockResolvedValue([
      {
        id: 'openai::gpt-4o',
        providerId: 'openai',
        apiModelId: 'gpt-4o',
        presetModelId: 'gpt-4o',
        name: 'GPT-4o',
        capabilities: [MODEL_CAPABILITY.FUNCTION_CALL],
        supportsStreaming: true,
        isEnabled: true,
        isHidden: false,
        isDeprecated: false
      }
    ])
    vi.mocked(fetchResolvedProviderModels).mockResolvedValue([
      {
        id: 'openai::gpt-4o',
        providerId: 'openai',
        apiModelId: 'gpt-4o',
        presetModelId: 'gpt-4o',
        name: 'GPT-4o',
        capabilities: [MODEL_CAPABILITY.FUNCTION_CALL],
        supportsStreaming: true,
        isEnabled: true,
        isHidden: false
      }
    ])

    const preview = await buildModelListSyncPreview({
      providerId: 'openai'
    })

    expect(preview.missing).toEqual([])
  })

  it('marks deprecated registry presets as missing when remote models omit them', async () => {
    vi.mocked(dataApiService.get).mockResolvedValue([
      {
        id: 'openai::gpt-4o',
        providerId: 'openai',
        apiModelId: 'gpt-4o',
        presetModelId: 'gpt-4o',
        name: 'GPT-4o',
        capabilities: [MODEL_CAPABILITY.FUNCTION_CALL],
        supportsStreaming: true,
        isEnabled: true,
        isHidden: false,
        isDeprecated: true
      }
    ])
    vi.mocked(fetchResolvedProviderModels).mockResolvedValue([])

    const preview = await buildModelListSyncPreview({
      providerId: 'openai'
    })

    expect(preview.missing.map((item) => item.model.id)).toEqual(['openai::gpt-4o'])
    expect(preview.missing.map((item) => item.removalReason)).toEqual(['missing_from_provider'])
  })
})
