import { describe, expect, test } from 'bun:test'
import {
  buildTool,
  toolMatchesName,
  findToolByName,
  getEmptyToolPermissionContext,
  filterToolProgressMessages,
} from '../Tool'

// Minimal tool definition for testing buildTool
function makeMinimalToolDef(overrides: Record<string, unknown> = {}) {
  return {
    name: 'TestTool',
    inputSchema: { type: 'object' as const } as any,
    maxResultSizeChars: 10000,
    call: async () => ({ data: 'ok' }),
    description: async () => 'A test tool',
    prompt: async () => 'test prompt',
    mapToolResultToToolResultBlockParam: (
      content: unknown,
      toolUseID: string,
    ) => ({
      type: 'tool_result' as const,
      tool_use_id: toolUseID,
      content: String(content),
    }),
    renderToolUseMessage: () => null,
    ...overrides,
  }
}

describe('buildTool', () => {
  test('fills in default isEnabled as true', () => {
    const tool = buildTool(makeMinimalToolDef())
    expect(tool.isEnabled()).toBe(true)
  })

  test('fills in default isConcurrencySafe as false', () => {
    const tool = buildTool(makeMinimalToolDef())
    expect(tool.isConcurrencySafe({})).toBe(false)
  })

  test('fills in default isReadOnly as false', () => {
    const tool = buildTool(makeMinimalToolDef())
    expect(tool.isReadOnly({})).toBe(false)
  })

  test('fills in default isDestructive as false', () => {
    const tool = buildTool(makeMinimalToolDef())
    expect(tool.isDestructive!({})).toBe(false)
  })

  test('fills in default checkPermissions as allow', async () => {
    const tool = buildTool(makeMinimalToolDef())
    const input = { foo: 'bar' }
    const result = await tool.checkPermissions(input, {} as any)
    expect(result).toEqual({ behavior: 'allow', updatedInput: input })
  })

  test('fills in default userFacingName from tool name', () => {
    const tool = buildTool(makeMinimalToolDef())
    expect(tool.userFacingName(undefined)).toBe('TestTool')
  })

  test('fills in default toAutoClassifierInput as empty string', () => {
    const tool = buildTool(makeMinimalToolDef())
    expect(tool.toAutoClassifierInput({})).toBe('')
  })

  test('preserves explicitly provided methods', () => {
    const tool = buildTool(
      makeMinimalToolDef({
        isEnabled: () => false,
        isConcurrencySafe: () => true,
        isReadOnly: () => true,
      }),
    )
    expect(tool.isEnabled()).toBe(false)
    expect(tool.isConcurrencySafe({})).toBe(true)
    expect(tool.isReadOnly({})).toBe(true)
  })

  test('preserves all non-defaultable properties', () => {
    const tool = buildTool(makeMinimalToolDef())
    expect(tool.name).toBe('TestTool')
    expect(tool.maxResultSizeChars).toBe(10000)
    expect(typeof tool.call).toBe('function')
    expect(typeof tool.description).toBe('function')
    expect(typeof tool.prompt).toBe('function')
  })
})

describe('toolMatchesName', () => {
  test('returns true for exact name match', () => {
    expect(toolMatchesName({ name: 'Bash' }, 'Bash')).toBe(true)
  })

  test('returns false for non-matching name', () => {
    expect(toolMatchesName({ name: 'Bash' }, 'Read')).toBe(false)
  })

  test('returns true when name matches an alias', () => {
    expect(
      toolMatchesName(
        { name: 'Bash', aliases: ['BashTool', 'Shell'] },
        'BashTool',
      ),
    ).toBe(true)
  })

  test('returns false when aliases is undefined', () => {
    expect(toolMatchesName({ name: 'Bash' }, 'BashTool')).toBe(false)
  })

  test('returns false when aliases is empty', () => {
    expect(toolMatchesName({ name: 'Bash', aliases: [] }, 'BashTool')).toBe(
      false,
    )
  })
})

describe('findToolByName', () => {
  const mockTools = [
    buildTool(makeMinimalToolDef({ name: 'Bash' })),
    buildTool(makeMinimalToolDef({ name: 'Read', aliases: ['FileRead'] })),
    buildTool(makeMinimalToolDef({ name: 'Edit' })),
  ]

  test('finds tool by primary name', () => {
    const tool = findToolByName(mockTools, 'Bash')
    expect(tool).toBeDefined()
    expect(tool!.name).toBe('Bash')
  })

  test('finds tool by alias', () => {
    const tool = findToolByName(mockTools, 'FileRead')
    expect(tool).toBeDefined()
    expect(tool!.name).toBe('Read')
  })

  test('returns undefined when no match', () => {
    expect(findToolByName(mockTools, 'NonExistent')).toBeUndefined()
  })

  test('returns first match when duplicates exist', () => {
    const dupeTools = [
      buildTool(makeMinimalToolDef({ name: 'Bash', maxResultSizeChars: 100 })),
      buildTool(makeMinimalToolDef({ name: 'Bash', maxResultSizeChars: 200 })),
    ]
    const tool = findToolByName(dupeTools, 'Bash')
    expect(tool!.maxResultSizeChars).toBe(100)
  })
})

describe('getEmptyToolPermissionContext', () => {
  test('returns default permission mode', () => {
    const ctx = getEmptyToolPermissionContext()
    expect(ctx.mode).toBe('default')
  })

  test('returns empty maps and arrays', () => {
    const ctx = getEmptyToolPermissionContext()
    expect(ctx.additionalWorkingDirectories.size).toBe(0)
    expect(ctx.alwaysAllowRules).toEqual({})
    expect(ctx.alwaysDenyRules).toEqual({})
    expect(ctx.alwaysAskRules).toEqual({})
  })

  test('returns isBypassPermissionsModeAvailable as true', () => {
    const ctx = getEmptyToolPermissionContext()
    expect(ctx.isBypassPermissionsModeAvailable).toBe(true)
  })
})

describe('filterToolProgressMessages', () => {
  test('filters out hook_progress messages', () => {
    const messages = [
      { data: { type: 'hook_progress', hookName: 'pre' } },
      { data: { type: 'tool_progress', toolName: 'Bash' } },
    ] as any[]
    const result = filterToolProgressMessages(messages)
    expect(result).toHaveLength(1)
    expect((result[0]!.data as any).type).toBe('tool_progress')
  })

  test('keeps tool progress messages', () => {
    const messages = [
      { data: { type: 'tool_progress', toolName: 'Bash' } },
      { data: { type: 'tool_progress', toolName: 'Read' } },
    ] as any[]
    const result = filterToolProgressMessages(messages)
    expect(result).toHaveLength(2)
  })

  test('returns empty array for empty input', () => {
    expect(filterToolProgressMessages([])).toEqual([])
  })

  test('handles messages without type field', () => {
    const messages = [
      { data: { toolName: 'Bash' } },
      { data: { type: 'hook_progress' } },
    ] as any[]
    const result = filterToolProgressMessages(messages)
    expect(result).toHaveLength(1)
  })
})
