import { describe, it, expect } from 'vitest'
import { addToolError } from '../utils'
import type { AgentMemory } from '../types'
import type { ToolCallAction } from '../../actions/types'
import { MemoryManager } from '../manager'
import { rateLimitExceeded, credentialsMissing, validationError } from '../../../tools/types'

describe('addToolError', () => {
  const createMemoryManager = (): { manager: MemoryManager; memory: AgentMemory } => {
    const memory: AgentMemory = {
      sessionMemory: {},
      history: [],
      compactionLog: []
    }
    return { manager: new MemoryManager(memory), memory }
  }

  it('adds error entry to memory history', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-123',
      name: 'searchDatabase',
      input: { query: 'test' }
    }

    addToolError(manager, action, 'Database connection failed')

    // Test actual behavior - error is in history
    expect(memory.history).toHaveLength(1)
    expect(memory.history[0].type).toBe('error')
  })

  it('creates error entry with correct error details', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-456',
      name: 'fetchData',
      input: {}
    }

    addToolError(manager, action, 'Network timeout')

    // Test actual behavior - error content is correct
    const errorEntry = memory.history[0]
    expect(errorEntry.type).toBe('error')

    const errorData = JSON.parse(errorEntry.content)
    expect(errorData.error).toBe('Network timeout')
    expect(errorData.toolName).toBe('fetchData')
    expect(errorData.toolCallId).toBe('tool-call-456')
  })

  it('includes error message in content', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-789',
      name: 'calculateSum',
      input: { a: 1, b: 2 }
    }

    addToolError(manager, action, 'Invalid input parameters')

    // Test actual behavior - error message is in content
    const errorEntry = memory.history[0]
    const content = JSON.parse(errorEntry.content)
    expect(content.error).toBe('Invalid input parameters')
  })

  it('includes tool name in content', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-abc',
      name: 'sendEmail',
      input: { to: 'test@example.com' }
    }

    addToolError(manager, action, 'SMTP server unreachable')

    // Test actual behavior - tool name is in content
    const errorEntry = memory.history[0]
    const content = JSON.parse(errorEntry.content)
    expect(content.toolName).toBe('sendEmail')
  })

  it('includes tool call ID in content', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-xyz',
      name: 'validateInput',
      input: { data: 'test' }
    }

    addToolError(manager, action, 'Validation failed')

    // Test actual behavior - tool call ID is in content
    const errorEntry = memory.history[0]
    const content = JSON.parse(errorEntry.content)
    expect(content.toolCallId).toBe('tool-call-xyz')
  })

  it('adds timestamp to error entry', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-123',
      name: 'testTool',
      input: {}
    }

    const beforeTimestamp = Date.now()
    addToolError(manager, action, 'Test error')
    const afterTimestamp = Date.now()

    // Test actual behavior - timestamp is added
    const errorEntry = memory.history[0]
    expect(errorEntry.timestamp).toBeGreaterThanOrEqual(beforeTimestamp)
    expect(errorEntry.timestamp).toBeLessThanOrEqual(afterTimestamp)
  })

  it('handles complex error messages', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-456',
      name: 'processData',
      input: { items: [1, 2, 3] }
    }

    const complexError = 'Error: Failed to process item at index 2. Reason: Invalid format. Expected number, got string.'

    addToolError(manager, action, complexError)

    // Test actual behavior - complex error message is preserved
    const errorEntry = memory.history[0]
    const content = JSON.parse(errorEntry.content)
    expect(content.error).toBe(complexError)
  })

  it('handles special characters in error messages', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-789',
      name: 'parseJson',
      input: { data: '{"key": "value"}' }
    }

    const errorWithSpecialChars = 'Unexpected token "}" at position 42'

    addToolError(manager, action, errorWithSpecialChars)

    // Test actual behavior - special chars are handled correctly
    const errorEntry = memory.history[0]
    const content = JSON.parse(errorEntry.content)
    expect(content.error).toBe(errorWithSpecialChars)
  })

  it('handles empty error message', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-empty',
      name: 'testTool',
      input: {}
    }

    addToolError(manager, action, '')

    // Test actual behavior - empty error is stored
    const errorEntry = memory.history[0]
    const content = JSON.parse(errorEntry.content)
    expect(content.error).toBe('')
  })

  it('creates valid JSON in content field', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-json',
      name: 'testTool',
      input: {}
    }

    addToolError(manager, action, 'Test error')

    // Test actual behavior - content is valid JSON
    const errorEntry = memory.history[0]

    // Should be valid JSON that can be parsed
    expect(() => JSON.parse(errorEntry.content)).not.toThrow()

    const parsed = JSON.parse(errorEntry.content)
    expect(parsed).toHaveProperty('error')
    expect(parsed).toHaveProperty('toolName')
    expect(parsed).toHaveProperty('toolCallId')
  })

  it('includes metadata for retryable ToolingError', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-meta-1',
      name: 'attio_search_records',
      input: {}
    }

    const error = rateLimitExceeded('Attio')
    addToolError(manager, action, error.message, 1, null, {
      errorType: error.errorType,
      severity: error.severity,
      isRetryable: error.isRetryable()
    })

    const content = JSON.parse(memory.history[0].content)
    expect(content.errorType).toBe('rate_limit_exceeded')
    expect(content.severity).toBe('warning')
    expect(content.isRetryable).toBe(true)
  })

  it('includes metadata for non-retryable ToolingError', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-meta-2',
      name: 'gmail_send_email',
      input: {}
    }

    const error = credentialsMissing('Gmail')
    addToolError(manager, action, error.message, 1, null, {
      errorType: error.errorType,
      severity: error.severity,
      isRetryable: error.isRetryable()
    })

    const content = JSON.parse(memory.history[0].content)
    expect(content.errorType).toBe('credentials_missing')
    expect(content.severity).toBe('critical')
    expect(content.isRetryable).toBe(false)
  })

  it('omits metadata fields when metadata is undefined', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-meta-3',
      name: 'testTool',
      input: {}
    }

    addToolError(manager, action, 'Plain error', 1, null, undefined)

    const content = JSON.parse(memory.history[0].content)
    expect(content).not.toHaveProperty('errorType')
    expect(content).not.toHaveProperty('severity')
    expect(content).not.toHaveProperty('isRetryable')
  })

  it('includes isRetryable even when false', () => {
    const { manager, memory } = createMemoryManager()
    const action: ToolCallAction = {
      type: 'tool-call',
      id: 'tool-call-meta-4',
      name: 'testTool',
      input: {}
    }

    const error = validationError('Invalid input')
    addToolError(manager, action, error.message, 1, null, {
      errorType: error.errorType,
      severity: error.severity,
      isRetryable: error.isRetryable()
    })

    const content = JSON.parse(memory.history[0].content)
    expect(content.isRetryable).toBe(false)
    expect(content.errorType).toBe('validation_error')
    expect(content.severity).toBe('info')
  })
})
