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

const resolveOpenAICompatClientConfigMock = vi.hoisted(() =>
  vi.fn(async () => ({
    providerId: 'openai-compatible:node-1',
    baseUrl: 'https://compat.example.com/v1',
    apiKey: 'sk-test',
  })),
)

vi.mock('@/lib/model-gateway/openai-compat/common', () => ({
  resolveOpenAICompatClientConfig: resolveOpenAICompatClientConfigMock,
}))

import { probeModelLlmProtocol } from '@/lib/user-api/model-llm-protocol-probe'

describe('user-api model llm protocol probe', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('returns responses protocol when responses endpoint succeeds', async () => {
    const fetchMock = vi.fn(async () => new Response(JSON.stringify({ id: 'resp_1' }), { status: 200 }))
    vi.stubGlobal('fetch', fetchMock)

    const result = await probeModelLlmProtocol({
      userId: 'user-1',
      providerId: 'openai-compatible:node-1',
      modelId: 'gpt-4.1-mini',
    })

    expect(result.success).toBe(true)
    if (!result.success) return
    expect(result.protocol).toBe('responses')
    expect(fetchMock).toHaveBeenCalledTimes(1)
    const firstCall = fetchMock.mock.calls[0] as unknown[] | undefined
    expect(String(firstCall?.[0])).toBe('https://compat.example.com/v1/responses')
  })

  it('returns chat-completions when responses is unsupported and chat succeeds', async () => {
    const fetchMock = vi.fn(async (input: unknown) => {
      const url = String(input)
      if (url.endsWith('/responses')) return new Response('not found', { status: 404 })
      if (url.endsWith('/chat/completions')) return new Response(JSON.stringify({ id: 'chatcmpl_1' }), { status: 200 })
      return new Response('unexpected', { status: 500 })
    })
    vi.stubGlobal('fetch', fetchMock)

    const result = await probeModelLlmProtocol({
      userId: 'user-1',
      providerId: 'openai-compatible:node-1',
      modelId: 'gpt-4.1-mini',
    })

    expect(result.success).toBe(true)
    if (!result.success) return
    expect(result.protocol).toBe('chat-completions')
    expect(result.traces.map((trace) => trace.endpoint)).toEqual(['responses', 'chat-completions'])
  })

  it('returns chat-completions when responses is rate limited but chat succeeds', async () => {
    const fetchMock = vi.fn(async (input: unknown) => {
      const url = String(input)
      if (url.endsWith('/responses')) return new Response('rate limit', { status: 429 })
      if (url.endsWith('/chat/completions')) return new Response(JSON.stringify({ id: 'chatcmpl_1' }), { status: 200 })
      return new Response('unexpected', { status: 500 })
    })
    vi.stubGlobal('fetch', fetchMock)

    const result = await probeModelLlmProtocol({
      userId: 'user-1',
      providerId: 'openai-compatible:node-1',
      modelId: 'gpt-4.1-mini',
    })

    expect(result.success).toBe(true)
    if (!result.success) return
    expect(result.protocol).toBe('chat-completions')
    expect(result.traces[0]?.status).toBe(429)
    expect(result.traces[1]?.status).toBe(200)
  })

  it('treats responses 5xx with not-implemented style message as unsupported', async () => {
    const fetchMock = vi.fn(async (input: unknown) => {
      const url = String(input)
      if (url.endsWith('/responses')) {
        return new Response(JSON.stringify({
          error: {
            message: 'not implemented (request id: x)',
            code: 'local:convert_request_failed',
          },
        }), { status: 500 })
      }
      if (url.endsWith('/chat/completions')) {
        return new Response(JSON.stringify({ id: 'chatcmpl_1' }), { status: 200 })
      }
      return new Response('unexpected', { status: 500 })
    })
    vi.stubGlobal('fetch', fetchMock)

    const result = await probeModelLlmProtocol({
      userId: 'user-1',
      providerId: 'openai-compatible:node-1',
      modelId: 'gpt-4.1-mini',
    })

    expect(result.success).toBe(true)
    if (!result.success) return
    expect(result.protocol).toBe('chat-completions')
  })

  it('treats responses 400 with unsupported keywords as unsupported', async () => {
    const fetchMock = vi.fn(async (input: unknown) => {
      const url = String(input)
      if (url.endsWith('/responses')) {
        return new Response(JSON.stringify({ error: { message: 'unknown endpoint /responses' } }), { status: 400 })
      }
      if (url.endsWith('/chat/completions')) {
        return new Response(JSON.stringify({ id: 'chatcmpl_1' }), { status: 200 })
      }
      return new Response('unexpected', { status: 500 })
    })
    vi.stubGlobal('fetch', fetchMock)

    const result = await probeModelLlmProtocol({
      userId: 'user-1',
      providerId: 'openai-compatible:node-1',
      modelId: 'gpt-4.1-mini',
    })

    expect(result.success).toBe(true)
    if (!result.success) return
    expect(result.protocol).toBe('chat-completions')
  })

  it('returns chat-completions when responses 422 has no unsupported keywords but chat succeeds', async () => {
    const fetchMock = vi.fn(async (input: unknown) => {
      const url = String(input)
      if (url.endsWith('/responses')) {
        return new Response(JSON.stringify({ error: { message: 'invalid payload' } }), { status: 422 })
      }
      if (url.endsWith('/chat/completions')) {
        return new Response(JSON.stringify({ id: 'chatcmpl_1' }), { status: 200 })
      }
      return new Response('unexpected', { status: 500 })
    })
    vi.stubGlobal('fetch', fetchMock)

    const result = await probeModelLlmProtocol({
      userId: 'user-1',
      providerId: 'openai-compatible:node-1',
      modelId: 'gpt-4.1-mini',
    })

    expect(result.success).toBe(true)
    if (!result.success) return
    expect(result.protocol).toBe('chat-completions')
    expect(result.traces[0]?.status).toBe(422)
    expect(result.traces[1]?.status).toBe(200)
  })

  it('returns auth failure when responses and chat both return 401', async () => {
    const fetchMock = vi.fn(async () => new Response('unauthorized', { status: 401 }))
    vi.stubGlobal('fetch', fetchMock)

    const result = await probeModelLlmProtocol({
      userId: 'user-1',
      providerId: 'openai-compatible:node-1',
      modelId: 'gpt-4.1-mini',
    })

    expect(result.success).toBe(false)
    if (result.success) return
    expect(result.code).toBe('PROBE_AUTH_FAILED')
    expect(fetchMock).toHaveBeenCalledTimes(2)
  })

  it('returns chat-completions when responses auth fails but chat succeeds', async () => {
    const fetchMock = vi.fn(async (input: unknown) => {
      const url = String(input)
      if (url.endsWith('/responses')) return new Response('unauthorized', { status: 401 })
      if (url.endsWith('/chat/completions')) return new Response(JSON.stringify({ id: 'chatcmpl_1' }), { status: 200 })
      return new Response('unexpected', { status: 500 })
    })
    vi.stubGlobal('fetch', fetchMock)

    const result = await probeModelLlmProtocol({
      userId: 'user-1',
      providerId: 'openai-compatible:node-1',
      modelId: 'gpt-4.1-mini',
    })

    expect(result.success).toBe(true)
    if (!result.success) return
    expect(result.protocol).toBe('chat-completions')
    expect(fetchMock).toHaveBeenCalledTimes(2)
  })
})
