import { describe, expect, test } from 'bun:test'
import { anthropicMessagesToOpenAI } from '../openaiConvertMessages.js'
import type { UserMessage, AssistantMessage } from '../../types/message.js'

// Helpers to create internal-format messages
function makeUserMsg(content: string | any[]): UserMessage {
  return {
    type: 'user',
    uuid: '00000000-0000-0000-0000-000000000000',
    message: { role: 'user', content },
  } as UserMessage
}

function makeAssistantMsg(content: string | any[]): AssistantMessage {
  return {
    type: 'assistant',
    uuid: '00000000-0000-0000-0000-000000000001',
    message: { role: 'assistant', content },
  } as AssistantMessage
}

describe('anthropicMessagesToOpenAI', () => {
  test('converts system prompt to system message', () => {
    const result = anthropicMessagesToOpenAI([makeUserMsg('hello')], [
      'You are helpful.',
    ] as any)
    expect(result[0]).toEqual({ role: 'system', content: 'You are helpful.' })
  })

  test('joins multiple system prompt strings', () => {
    const result = anthropicMessagesToOpenAI([makeUserMsg('hi')], [
      'Part 1',
      'Part 2',
    ] as any)
    expect(result[0]).toEqual({ role: 'system', content: 'Part 1\n\nPart 2' })
  })

  test('skips empty system prompt', () => {
    const result = anthropicMessagesToOpenAI([makeUserMsg('hi')], [] as any)
    expect(result[0].role).toBe('user')
  })

  test('converts simple user text message', () => {
    const result = anthropicMessagesToOpenAI(
      [makeUserMsg('hello world')],
      [] as any,
    )
    expect(result).toEqual([{ role: 'user', content: 'hello world' }])
  })

  test('converts user message with content array', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg([
          { type: 'text', text: 'line 1' },
          { type: 'text', text: 'line 2' },
        ]),
      ],
      [] as any,
    )
    expect(result).toEqual([{ role: 'user', content: 'line 1\nline 2' }])
  })

  test('converts assistant message with text', () => {
    const result = anthropicMessagesToOpenAI(
      [makeAssistantMsg('response text')],
      [] as any,
    )
    expect(result).toEqual([{ role: 'assistant', content: 'response text' }])
  })

  test('converts assistant message with tool_use', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeAssistantMsg([
          { type: 'text', text: 'Let me help.' },
          {
            type: 'tool_use' as const,
            id: 'toolu_123',
            name: 'bash',
            input: { command: 'ls' },
          },
        ]),
      ],
      [] as any,
    )
    expect(result).toEqual([
      {
        role: 'assistant',
        content: 'Let me help.',
        tool_calls: [
          {
            id: 'toolu_123',
            type: 'function',
            function: { name: 'bash', arguments: '{"command":"ls"}' },
          },
        ],
      },
    ])
  })

  test('converts tool_result to tool message', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg([
          {
            type: 'tool_result' as const,
            tool_use_id: 'toolu_123',
            content: 'file1.txt\nfile2.txt',
          },
        ]),
      ],
      [] as any,
    )
    expect(result).toEqual([
      {
        role: 'tool',
        tool_call_id: 'toolu_123',
        content: 'file1.txt\nfile2.txt',
      },
    ])
  })

  test('preserves thinking blocks as reasoning_content', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'internal thoughts...' },
          { type: 'text', text: 'visible response' },
        ]),
      ],
      [] as any,
    )
    expect(result).toEqual([
      {
        role: 'assistant',
        content: 'visible response',
        reasoning_content: 'internal thoughts...',
      },
    ] as any)
  })

  test('handles full conversation with tools', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('list files'),
        makeAssistantMsg([
          {
            type: 'tool_use' as const,
            id: 'toolu_abc',
            name: 'bash',
            input: { command: 'ls' },
          },
        ]),
        makeUserMsg([
          {
            type: 'tool_result' as const,
            tool_use_id: 'toolu_abc',
            content: 'file.txt',
          },
        ]),
      ],
      ['You are helpful.'] as any,
    )

    expect(result).toHaveLength(4)
    expect(result[0].role).toBe('system')
    expect(result[1].role).toBe('user')
    expect(result[2].role).toBe('assistant')
    expect((result[2] as any).tool_calls).toBeDefined()
    expect(result[3].role).toBe('tool')
  })

  test('converts base64 image to image_url', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg([
          { type: 'text', text: 'what is this?' },
          {
            type: 'image' as const,
            source: {
              type: 'base64',
              media_type: 'image/png',
              data: 'iVBORw0KGgo=',
            },
          },
        ]),
      ],
      [] as any,
    )
    expect(result).toEqual([
      {
        role: 'user',
        content: [
          { type: 'text', text: 'what is this?' },
          {
            type: 'image_url',
            image_url: { url: 'data:image/png;base64,iVBORw0KGgo=' },
          },
        ],
      },
    ])
  })

  test('converts url image to image_url', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg([
          {
            type: 'image' as const,
            source: {
              type: 'url',
              url: 'https://example.com/img.png',
            },
          },
        ]),
      ],
      [] as any,
    )
    expect(result).toEqual([
      {
        role: 'user',
        content: [
          {
            type: 'image_url',
            image_url: { url: 'https://example.com/img.png' },
          },
        ],
      },
    ])
  })

  test('converts image-only message without text', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg([
          {
            type: 'image' as const,
            source: {
              type: 'base64',
              media_type: 'image/jpeg',
              data: '/9j/4AAQ',
            },
          },
        ]),
      ],
      [] as any,
    )
    expect(result).toEqual([
      {
        role: 'user',
        content: [
          {
            type: 'image_url',
            image_url: { url: 'data:image/jpeg;base64,/9j/4AAQ' },
          },
        ],
      },
    ])
  })

  test('defaults to image/png when media_type is missing', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg([
          {
            type: 'image' as const,
            source: {
              type: 'base64',
              data: 'ABC123',
            },
          },
        ]),
      ],
      [] as any,
    )
    expect((result[0].content as any[])[0].image_url.url).toBe(
      'data:image/png;base64,ABC123',
    )
  })
})

describe('DeepSeek thinking mode (enableThinking)', () => {
  test('preserves thinking block as reasoning_content when enabled', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('question'),
        makeAssistantMsg([
          {
            type: 'thinking' as const,
            thinking: 'Let me reason about this...',
          },
          { type: 'text', text: 'The answer is 42.' },
        ]),
      ],
      [] as any,
      { enableThinking: true },
    )
    // Should have: user, assistant with reasoning_content
    expect(result).toHaveLength(2)
    expect(result[0].role).toBe('user')
    const assistant = result[1] as any
    expect(assistant.role).toBe('assistant')
    expect(assistant.content).toBe('The answer is 42.')
    expect(assistant.reasoning_content).toBe('Let me reason about this...')
  })

  test('preserves thinking block as reasoning_content even without enableThinking', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'internal thoughts...' },
          { type: 'text', text: 'visible response' },
        ]),
      ],
      [] as any,
    )
    const assistant = result[0] as any
    expect(assistant.content).toBe('visible response')
    expect(assistant.reasoning_content).toBe('internal thoughts...')
  })

  test('preserves reasoning_content with tool_calls in same turn', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('what is the weather?'),
        makeAssistantMsg([
          {
            type: 'thinking' as const,
            thinking: 'I need to call the weather tool.',
          },
          { type: 'text', text: '' },
          {
            type: 'tool_use' as const,
            id: 'toolu_001',
            name: 'get_weather',
            input: { location: 'Hangzhou' },
          },
        ]),
        makeUserMsg([
          {
            type: 'tool_result' as const,
            tool_use_id: 'toolu_001',
            content: 'Cloudy 7~13°C',
          },
        ]),
      ],
      [] as any,
      { enableThinking: true },
    )

    // Find the assistant message
    const assistants = result.filter(m => m.role === 'assistant')
    expect(assistants.length).toBe(1)
    const assistant = assistants[0] as any
    expect(assistant.reasoning_content).toBe('I need to call the weather tool.')
    expect(assistant.tool_calls).toBeDefined()
    expect(assistant.tool_calls[0].function.name).toBe('get_weather')
  })

  test('always preserves reasoning_content from all turns', () => {
    const result = anthropicMessagesToOpenAI(
      [
        // Turn 1: user → assistant (with thinking)
        makeUserMsg('question 1'),
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'Turn 1 reasoning...' },
          { type: 'text', text: 'Turn 1 answer' },
        ]),
        // Turn 2: new user message → reasoning should still be preserved
        // (DeepSeek requires reasoning_content to be passed back when tool calls are involved)
        makeUserMsg('question 2'),
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'Turn 2 reasoning...' },
          { type: 'text', text: 'Turn 2 answer' },
        ]),
      ],
      [] as any,
      { enableThinking: true },
    )

    const assistants = result.filter(m => m.role === 'assistant')
    // Both turns preserve reasoning_content (DeepSeek API requires it for tool calls)
    expect((assistants[0] as any).reasoning_content).toBe('Turn 1 reasoning...')
    expect((assistants[0] as any).content).toBe('Turn 1 answer')
    expect((assistants[1] as any).reasoning_content).toBe('Turn 2 reasoning...')
    expect((assistants[1] as any).content).toBe('Turn 2 answer')
  })

  test('preserves reasoning_content in multi-iteration tool call within same turn', () => {
    // Simulates a full DeepSeek tool call iteration:
    // user → assistant(thinking+tool_call) → tool_result → assistant(thinking+tool_call) → tool_result → assistant(thinking+text)
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg("tomorrow's weather in Hangzhou"),
        // Iteration 1: thinking + tool call
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'I need the date first.' },
          {
            type: 'tool_use' as const,
            id: 'toolu_001',
            name: 'get_date',
            input: {},
          },
        ]),
        makeUserMsg([
          {
            type: 'tool_result' as const,
            tool_use_id: 'toolu_001',
            content: '2026-04-08',
          },
        ]),
        // Iteration 2: thinking + tool call
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'Now I can get the weather.' },
          {
            type: 'tool_use' as const,
            id: 'toolu_002',
            name: 'get_weather',
            input: { location: 'Hangzhou', date: '2026-04-08' },
          },
        ]),
        makeUserMsg([
          {
            type: 'tool_result' as const,
            tool_use_id: 'toolu_002',
            content: 'Cloudy 7~13°C',
          },
        ]),
        // Iteration 3: thinking + final answer
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'I have the info now.' },
          { type: 'text', text: 'Tomorrow will be cloudy, 7-13°C.' },
        ]),
      ],
      [] as any,
      { enableThinking: true },
    )

    const assistants = result.filter(m => m.role === 'assistant')
    expect(assistants.length).toBe(3)
    // All iterations within the same turn preserve reasoning
    expect((assistants[0] as any).reasoning_content).toBe(
      'I need the date first.',
    )
    expect((assistants[1] as any).reasoning_content).toBe(
      'Now I can get the weather.',
    )
    expect((assistants[2] as any).reasoning_content).toBe(
      'I have the info now.',
    )
  })

  test('handles multiple thinking blocks in single assistant message', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('question'),
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'First thought.' },
          { type: 'thinking' as const, thinking: 'Second thought.' },
          { type: 'text', text: 'Final answer.' },
        ]),
      ],
      [] as any,
      { enableThinking: true },
    )
    const assistant = result.filter(m => m.role === 'assistant')[0] as any
    expect(assistant.reasoning_content).toBe('First thought.\nSecond thought.')
  })

  test('preserves empty thinking blocks as reasoning_content: "" (DeepSeek v4 thinking mode)', () => {
    // DeepSeek v4 thinking mode sometimes returns reasoning_content: ""
    // when the model answers directly without reasoning. The empty value
    // must be echoed back in the next request — otherwise DeepSeek returns
    // 400 ("reasoning_content ... must be passed back"). See issue #399.
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('question'),
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: '' },
          { type: 'text', text: 'Answer.' },
        ]),
      ],
      [] as any,
      { enableThinking: true },
    )
    const assistant = result.filter(m => m.role === 'assistant')[0] as any
    expect(assistant.reasoning_content).toBe('')
    expect(assistant.content).toBe('Answer.')
  })

  test('omits reasoning_content when no thinking block is present', () => {
    // No thinking block at all → no reasoning_content field on the
    // OpenAI-format assistant message (relevant for non-thinking models).
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('question'),
        makeAssistantMsg([{ type: 'text', text: 'Answer.' }]),
      ],
      [] as any,
    )
    const assistant = result.filter(m => m.role === 'assistant')[0] as any
    expect(assistant.reasoning_content).toBeUndefined()
    expect(assistant.content).toBe('Answer.')
  })

  // ── fix: reorder tool and user messages for OpenAI API compatibility (#168) ──

  test('tool messages come BEFORE user text when mixed in same turn', () => {
    // OpenAI requires: assistant(tool_calls) → tool → user
    // Bug: previously user text was emitted before tool messages
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('run ls'),
        makeAssistantMsg([
          {
            type: 'tool_use' as const,
            id: 'toolu_1',
            name: 'bash',
            input: { command: 'ls' },
          },
        ]),
        makeUserMsg([
          {
            type: 'tool_result' as const,
            tool_use_id: 'toolu_1',
            content: 'file.txt',
          },
          { type: 'text' as const, text: 'looks good' },
        ]),
      ],
      [] as any,
    )
    // Find the tool message and the user text message
    const toolIdx = result.findIndex(m => m.role === 'tool')
    const userTextIdx = result.findIndex(
      m =>
        m.role === 'user' &&
        typeof m.content === 'string' &&
        m.content.includes('looks good'),
    )
    expect(toolIdx).toBeGreaterThanOrEqual(0)
    expect(userTextIdx).toBeGreaterThanOrEqual(0)
    // Tool MUST come before user text
    expect(toolIdx).toBeLessThan(userTextIdx)
  })

  test('tool message immediately follows assistant tool_calls (no user message in between)', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('do something'),
        makeAssistantMsg([
          {
            type: 'tool_use' as const,
            id: 'toolu_2',
            name: 'bash',
            input: { command: 'pwd' },
          },
        ]),
        makeUserMsg([
          {
            type: 'tool_result' as const,
            tool_use_id: 'toolu_2',
            content: '/home/user',
          },
        ]),
      ],
      [] as any,
    )
    const assistantIdx = result.findIndex(
      m => m.role === 'assistant' && (m as any).tool_calls,
    )
    const toolIdx = result.findIndex(m => m.role === 'tool')
    expect(assistantIdx).toBeGreaterThanOrEqual(0)
    expect(toolIdx).toBe(assistantIdx + 1)
  })

  test('sets content to null when only thinking and tool_calls present', () => {
    const result = anthropicMessagesToOpenAI(
      [
        makeUserMsg('question'),
        makeAssistantMsg([
          { type: 'thinking' as const, thinking: 'Reasoning only.' },
          {
            type: 'tool_use' as const,
            id: 'toolu_001',
            name: 'bash',
            input: { command: 'ls' },
          },
        ]),
      ],
      [] as any,
      { enableThinking: true },
    )
    const assistant = result.filter(m => m.role === 'assistant')[0] as any
    expect(assistant.content).toBeNull()
    expect(assistant.reasoning_content).toBe('Reasoning only.')
    expect(assistant.tool_calls).toHaveLength(1)
  })
})
