import { describe, expect, test, beforeEach, afterEach, mock } from 'bun:test'
import {
  isOpenAIThinkingEnabled,
  buildOpenAIRequestBody,
} from '../requestBody.js'

// Re-register envUtils.js with correct isEnvDefinedFalsy and isEnvTruthy to
// override pollution from other test files (debug-tool-call, issue,
// break-cache, MagicDocs/prompts, SessionMemory/prompts, cacheStats) that
// mock this module without exporting isEnvDefinedFalsy.
mock.module('src/utils/envUtils.js', () => ({
  isEnvTruthy: (v: string | boolean | undefined): boolean => {
    if (!v) return false
    if (typeof v === 'boolean') return v
    return ['1', 'true', 'yes', 'on'].includes(v.toLowerCase().trim())
  },
  isEnvDefinedFalsy: (v: string | boolean | undefined): boolean => {
    if (v === undefined) return false
    if (typeof v === 'boolean') return !v
    if (!v) return false
    return ['0', 'false', 'no', 'off'].includes(v.toLowerCase().trim())
  },
}))

describe('isOpenAIThinkingEnabled', () => {
  const originalEnv = {
    OPENAI_ENABLE_THINKING: process.env.OPENAI_ENABLE_THINKING,
  }

  beforeEach(() => {
    // Clear env var before each test
    delete process.env.OPENAI_ENABLE_THINKING
  })

  afterEach(() => {
    // Restore original env var — delete key if it was originally undefined
    // to avoid leaking the env key into subsequent tests
    if (originalEnv.OPENAI_ENABLE_THINKING === undefined) {
      delete process.env.OPENAI_ENABLE_THINKING
    } else {
      process.env.OPENAI_ENABLE_THINKING = originalEnv.OPENAI_ENABLE_THINKING
    }
  })

  describe('OPENAI_ENABLE_THINKING env var', () => {
    test('returns true when OPENAI_ENABLE_THINKING=1', () => {
      process.env.OPENAI_ENABLE_THINKING = '1'
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(true)
    })

    test('returns true when OPENAI_ENABLE_THINKING=true', () => {
      process.env.OPENAI_ENABLE_THINKING = 'true'
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(true)
    })

    test('returns true when OPENAI_ENABLE_THINKING=yes', () => {
      process.env.OPENAI_ENABLE_THINKING = 'yes'
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(true)
    })

    test('returns true when OPENAI_ENABLE_THINKING=on', () => {
      process.env.OPENAI_ENABLE_THINKING = 'on'
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(true)
    })

    test('returns true when OPENAI_ENABLE_THINKING=TRUE (case insensitive)', () => {
      process.env.OPENAI_ENABLE_THINKING = 'TRUE'
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(true)
    })

    test('returns false when OPENAI_ENABLE_THINKING=0', () => {
      process.env.OPENAI_ENABLE_THINKING = '0'
      expect(isOpenAIThinkingEnabled('deepseek-reasoner')).toBe(false)
    })

    test('returns false when OPENAI_ENABLE_THINKING=false', () => {
      process.env.OPENAI_ENABLE_THINKING = 'false'
      expect(isOpenAIThinkingEnabled('deepseek-reasoner')).toBe(false)
    })

    test('returns false when OPENAI_ENABLE_THINKING is empty', () => {
      process.env.OPENAI_ENABLE_THINKING = ''
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(false)
    })

    test('returns false when OPENAI_ENABLE_THINKING is not set', () => {
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(false)
    })
  })

  describe('model name auto-detect', () => {
    test('returns true when model name is "deepseek-reasoner"', () => {
      expect(isOpenAIThinkingEnabled('deepseek-reasoner')).toBe(true)
    })

    test('returns true when model name contains "deepseek-reasoner" (case insensitive)', () => {
      expect(isOpenAIThinkingEnabled('DeepSeek-Reasoner')).toBe(true)
    })

    test('returns true when model name has prefix/suffix for deepseek-reasoner', () => {
      expect(isOpenAIThinkingEnabled('my-deepseek-reasoner-v1')).toBe(true)
    })

    test('returns true when model name is namespaced for deepseek-reasoner', () => {
      expect(isOpenAIThinkingEnabled('TokenService/deepseek-reasoner')).toBe(
        true,
      )
    })

    test('returns true when model name is "deepseek-v3.2"', () => {
      expect(isOpenAIThinkingEnabled('deepseek-v3.2')).toBe(true)
    })

    test('returns true when model name contains "deepseek-v3.2" (case insensitive)', () => {
      expect(isOpenAIThinkingEnabled('DeepSeek-V3.2')).toBe(true)
    })

    test('returns true when model name has prefix/suffix for deepseek-v3.2', () => {
      expect(isOpenAIThinkingEnabled('my-deepseek-v3.2-v1')).toBe(true)
    })

    test('returns true when model name is namespaced for deepseek-v3.2', () => {
      expect(isOpenAIThinkingEnabled('TokenService/deepseek-v3.2')).toBe(true)
    })

    test('returns true when model name is "deepseek-chat"', () => {
      expect(isOpenAIThinkingEnabled('deepseek-chat')).toBe(true)
    })

    test('returns true when model name is "deepseek-v3"', () => {
      expect(isOpenAIThinkingEnabled('deepseek-v3')).toBe(true)
    })

    test('returns true when model name is "deepseek-v4"', () => {
      expect(isOpenAIThinkingEnabled('deepseek-v4')).toBe(true)
    })

    test('returns true when model name is "deepseek-v4-pro"', () => {
      expect(isOpenAIThinkingEnabled('deepseek-v4-pro')).toBe(true)
    })

    test('returns true when model name is "deepseek-r1"', () => {
      expect(isOpenAIThinkingEnabled('deepseek-r1')).toBe(true)
    })

    test('returns true when model name contains "deepseek"', () => {
      expect(isOpenAIThinkingEnabled('deepseek-coder')).toBe(true)
    })

    test('returns true when model name is "mimo-v2-flash"', () => {
      expect(isOpenAIThinkingEnabled('mimo-v2-flash')).toBe(true)
    })

    test('returns true when model name is "mimo-v2-pro"', () => {
      expect(isOpenAIThinkingEnabled('mimo-v2-pro')).toBe(true)
    })

    test('returns true when model name is "mimo-v2.5-pro"', () => {
      expect(isOpenAIThinkingEnabled('mimo-v2.5-pro')).toBe(true)
    })

    test('returns true when model name contains "mimo"', () => {
      expect(isOpenAIThinkingEnabled('MiMo-V2-Omni')).toBe(true)
    })

    test('returns false when model name is "gpt-4o"', () => {
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(false)
    })

    test('returns false when model name is empty', () => {
      expect(isOpenAIThinkingEnabled('')).toBe(false)
    })
  })

  describe('priority and combined detection', () => {
    test('OPENAI_ENABLE_THINKING=1 enables thinking for any model', () => {
      process.env.OPENAI_ENABLE_THINKING = '1'
      expect(isOpenAIThinkingEnabled('gpt-4o')).toBe(true)
      expect(isOpenAIThinkingEnabled('deepseek-v3')).toBe(true)
      expect(isOpenAIThinkingEnabled('qwen-3')).toBe(true)
    })

    test('OPENAI_ENABLE_THINKING=false disables thinking even for deepseek-reasoner', () => {
      process.env.OPENAI_ENABLE_THINKING = 'false'
      expect(isOpenAIThinkingEnabled('deepseek-reasoner')).toBe(false)
    })

    test('OPENAI_ENABLE_THINKING=0 disables thinking even for deepseek-reasoner', () => {
      process.env.OPENAI_ENABLE_THINKING = '0'
      expect(isOpenAIThinkingEnabled('deepseek-reasoner')).toBe(false)
    })

    test('both conditions can enable thinking', () => {
      process.env.OPENAI_ENABLE_THINKING = '1'
      expect(isOpenAIThinkingEnabled('deepseek-reasoner')).toBe(true)
    })
  })
})

describe('buildOpenAIRequestBody — thinking params', () => {
  const baseParams = {
    model: 'deepseek-reasoner',
    messages: [{ role: 'user', content: 'hello' }],
    tools: [] as any[],
    toolChoice: undefined as any,
  } as any

  test('includes official DeepSeek API thinking format when enabled', () => {
    const body = buildOpenAIRequestBody({ ...baseParams, enableThinking: true })
    expect(body.thinking).toEqual({ type: 'enabled' })
  })

  test('includes vLLM/self-hosted thinking format when enabled', () => {
    const body = buildOpenAIRequestBody({ ...baseParams, enableThinking: true })
    expect(body.enable_thinking).toBe(true)
    expect(body.chat_template_kwargs).toEqual({
      thinking: true,
      enable_thinking: true,
    })
  })

  test('includes both formats simultaneously when enabled', () => {
    const body = buildOpenAIRequestBody({ ...baseParams, enableThinking: true })
    expect(body.thinking).toEqual({ type: 'enabled' })
    expect(body.enable_thinking).toBe(true)
    expect(body.chat_template_kwargs!.thinking).toBe(true)
  })

  test('does NOT include thinking params when disabled', () => {
    const body = buildOpenAIRequestBody({
      ...baseParams,
      enableThinking: false,
    })
    expect(body.thinking).toBeUndefined()
    expect(body.enable_thinking).toBeUndefined()
    expect(body.chat_template_kwargs).toBeUndefined()
  })

  test('always includes stream and stream_options', () => {
    const body = buildOpenAIRequestBody({
      ...baseParams,
      enableThinking: false,
    })
    expect(body.stream).toBe(true)
    expect(body.stream_options).toEqual({ include_usage: true })
  })

  test('includes temperature when thinking is off and override is set', () => {
    const body = buildOpenAIRequestBody({
      ...baseParams,
      enableThinking: false,
      temperatureOverride: 0.7,
    })
    expect(body.temperature).toBe(0.7)
  })

  test('excludes temperature when thinking is on even if override is set', () => {
    const body = buildOpenAIRequestBody({
      ...baseParams,
      enableThinking: true,
      temperatureOverride: 0.7,
    })
    expect(body.temperature).toBeUndefined()
  })

  test('excludes temperature when thinking is off and no override', () => {
    const body = buildOpenAIRequestBody({
      ...baseParams,
      enableThinking: false,
    })
    expect(body.temperature).toBeUndefined()
  })

  test('includes tools and tool_choice when tools are provided', () => {
    const body = buildOpenAIRequestBody({
      ...baseParams,
      tools: [{ type: 'function', function: { name: 'test' } }],
      toolChoice: 'auto',
      enableThinking: false,
    })
    expect(body.tools).toHaveLength(1)
    expect(body.tool_choice).toBe('auto')
  })

  test('excludes tools when empty', () => {
    const body = buildOpenAIRequestBody({
      ...baseParams,
      enableThinking: false,
    })
    expect(body.tools).toBeUndefined()
    expect(body.tool_choice).toBeUndefined()
  })
})
