import { describe, it, expect, beforeEach } from 'vitest';
import { ToolRegistry } from '../../../src/tools/registry.js';
import type { ToolWithMetadata, ToolMetadata, ToolCategory } from '../../../src/tools/types.js';

// 创建 mock 工具
function createMockToolWithMetadata(
  name: string,
  options: Partial<{
    category: ToolCategory;
    deferLoading: boolean;
    keywords: string[];
    description: string;
  }> = {}
): ToolWithMetadata {
  const metadata: ToolMetadata = {
    name,
    category: options.category ?? 'core',
    description: options.description ?? `Mock tool: ${name}`,
    keywords: options.keywords ?? [name],
    deferLoading: options.deferLoading ?? false,
  };

  return {
    name,
    description: metadata.description,
    parameters: { type: 'object', properties: {}, required: [] },
    execute: async () => ({ success: true, output: `executed ${name}` }),
    metadata,
  };
}

describe('ToolRegistry - 工具注册表', () => {
  let registry: ToolRegistry;

  beforeEach(() => {
    registry = new ToolRegistry();
  });

  describe('register / registerAll', () => {
    it('注册单个工具', () => {
      const tool = createMockToolWithMetadata('test_tool');
      registry.register(tool);

      expect(registry.has('test_tool')).toBe(true);
      expect(registry.size).toBe(1);
    });

    it('批量注册工具', () => {
      const tools = [
        createMockToolWithMetadata('tool_a'),
        createMockToolWithMetadata('tool_b'),
        createMockToolWithMetadata('tool_c'),
      ];
      registry.registerAll(tools);

      expect(registry.size).toBe(3);
      expect(registry.has('tool_a')).toBe(true);
      expect(registry.has('tool_b')).toBe(true);
      expect(registry.has('tool_c')).toBe(true);
    });

    it('同名工具覆盖注册', () => {
      const tool1 = createMockToolWithMetadata('test_tool', { description: 'version 1' });
      const tool2 = createMockToolWithMetadata('test_tool', { description: 'version 2' });

      registry.register(tool1);
      registry.register(tool2);

      expect(registry.size).toBe(1);
      const retrieved = registry.getTool('test_tool');
      expect(retrieved?.description).toBe('version 2');
    });
  });

  describe('getTool / getTools', () => {
    beforeEach(() => {
      registry.registerAll([
        createMockToolWithMetadata('read_file'),
        createMockToolWithMetadata('write_file'),
        createMockToolWithMetadata('bash'),
      ]);
    });

    it('获取存在的工具', () => {
      const tool = registry.getTool('read_file');
      expect(tool).toBeDefined();
      expect(tool?.name).toBe('read_file');
    });

    it('获取不存在的工具返回 undefined', () => {
      const tool = registry.getTool('non_existent');
      expect(tool).toBeUndefined();
    });

    it('批量获取工具', () => {
      const tools = registry.getTools(['read_file', 'bash']);
      expect(tools).toHaveLength(2);
      expect(tools.map((t) => t.name)).toContain('read_file');
      expect(tools.map((t) => t.name)).toContain('bash');
    });

    it('批量获取时跳过不存在的工具', () => {
      const tools = registry.getTools(['read_file', 'non_existent', 'bash']);
      expect(tools).toHaveLength(2);
    });

    it('批量获取空列表返回空数组', () => {
      const tools = registry.getTools([]);
      expect(tools).toHaveLength(0);
    });
  });

  describe('getCoreTools - 核心工具（非延迟加载）', () => {
    it('只返回 deferLoading=false 的工具', () => {
      registry.registerAll([
        createMockToolWithMetadata('core_tool_1', { deferLoading: false }),
        createMockToolWithMetadata('core_tool_2', { deferLoading: false }),
        createMockToolWithMetadata('deferred_tool', { deferLoading: true }),
      ]);

      const coreTools = registry.getCoreTools();

      expect(coreTools).toHaveLength(2);
      expect(coreTools.map((t) => t.name)).toContain('core_tool_1');
      expect(coreTools.map((t) => t.name)).toContain('core_tool_2');
      expect(coreTools.map((t) => t.name)).not.toContain('deferred_tool');
    });

    it('所有工具都是延迟加载时返回空数组', () => {
      registry.registerAll([
        createMockToolWithMetadata('tool_1', { deferLoading: true }),
        createMockToolWithMetadata('tool_2', { deferLoading: true }),
      ]);

      const coreTools = registry.getCoreTools();
      expect(coreTools).toHaveLength(0);
    });

    it('默认 deferLoading=false 作为核心工具', () => {
      registry.register(createMockToolWithMetadata('default_tool'));
      const coreTools = registry.getCoreTools();
      expect(coreTools).toHaveLength(1);
    });
  });

  describe('getAllTools / getAllMetadata', () => {
    beforeEach(() => {
      registry.registerAll([
        createMockToolWithMetadata('tool_a', { category: 'filesystem', deferLoading: false }),
        createMockToolWithMetadata('tool_b', { category: 'shell', deferLoading: true }),
        createMockToolWithMetadata('tool_c', { category: 'core', deferLoading: false }),
      ]);
    });

    it('获取所有工具', () => {
      const allTools = registry.getAllTools();
      expect(allTools).toHaveLength(3);
    });

    it('获取所有元数据', () => {
      const metadata = registry.getAllMetadata();

      expect(metadata).toHaveLength(3);
      expect(metadata.find((m) => m.name === 'tool_a')?.category).toBe('filesystem');
      expect(metadata.find((m) => m.name === 'tool_b')?.deferLoading).toBe(true);
    });
  });

  describe('has / size', () => {
    it('空注册表', () => {
      expect(registry.size).toBe(0);
      expect(registry.has('any')).toBe(false);
    });

    it('注册后正确检测', () => {
      registry.register(createMockToolWithMetadata('test'));

      expect(registry.size).toBe(1);
      expect(registry.has('test')).toBe(true);
      expect(registry.has('other')).toBe(false);
    });
  });

  describe('search - 工具搜索', () => {
    // 注意：searchTools 只搜索 deferLoading=true 的工具
    beforeEach(() => {
      registry.registerAll([
        createMockToolWithMetadata('read_file', {
          category: 'filesystem',
          keywords: ['read', 'file', 'open', 'cat'],
          description: '读取文件内容',
          deferLoading: true, // 必须为 true 才能被搜索
        }),
        createMockToolWithMetadata('write_file', {
          category: 'filesystem',
          keywords: ['write', 'file', 'save', 'create'],
          description: '写入文件内容',
          deferLoading: true,
        }),
        createMockToolWithMetadata('bash', {
          category: 'shell',
          keywords: ['bash', 'shell', 'command', 'execute', 'run'],
          description: '执行 bash 命令',
          deferLoading: true,
        }),
        createMockToolWithMetadata('glob', {
          category: 'filesystem',
          keywords: ['glob', 'pattern', 'find', 'search', 'file'],
          description: '搜索匹配模式的文件',
          deferLoading: true,
        }),
      ]);
    });

    it('按关键词搜索', () => {
      const results = registry.search('file');

      expect(results.length).toBeGreaterThan(0);
      // file 相关工具应该排在前面
      const fileTools = results.filter((r) => r.name.includes('file') || r.category === 'filesystem');
      expect(fileTools.length).toBeGreaterThan(0);
    });

    it('限制返回结果数量', () => {
      const results = registry.search('file', 2);
      expect(results.length).toBeLessThanOrEqual(2);
    });

    it('搜索 shell 相关', () => {
      const results = registry.search('shell');

      expect(results.length).toBeGreaterThan(0);
      expect(results.some((r) => r.name === 'bash')).toBe(true);
    });

    it('无匹配时返回空数组或低分结果', () => {
      const results = registry.search('xyznonexistent');
      // 可能返回空数组或低分结果，取决于搜索实现
      expect(Array.isArray(results)).toBe(true);
    });

    it('只搜索 deferLoading=true 的工具', () => {
      // 创建新的注册表
      const testRegistry = new ToolRegistry();
      testRegistry.registerAll([
        createMockToolWithMetadata('core_tool', {
          keywords: ['test'],
          deferLoading: false, // 核心工具，不被搜索
        }),
        createMockToolWithMetadata('deferred_tool', {
          keywords: ['test'],
          deferLoading: true, // 延迟加载，可被搜索
        }),
      ]);

      const results = testRegistry.search('test');

      expect(results).toHaveLength(1);
      expect(results[0].name).toBe('deferred_tool');
    });
  });

  describe('toBasicTool - 转换为基础工具类型', () => {
    it('转换后只包含基础属性', () => {
      const toolWithMeta = createMockToolWithMetadata('test', {
        category: 'filesystem',
        deferLoading: true,
        keywords: ['test', 'mock'],
      });
      registry.register(toolWithMeta);

      const basicTool = registry.getTool('test');

      expect(basicTool).toBeDefined();
      expect(basicTool).toHaveProperty('name');
      expect(basicTool).toHaveProperty('description');
      expect(basicTool).toHaveProperty('parameters');
      expect(basicTool).toHaveProperty('execute');
      // 不应该包含 metadata
      expect(basicTool).not.toHaveProperty('metadata');
    });

    it('execute 函数正常工作', async () => {
      registry.register(createMockToolWithMetadata('test'));
      const tool = registry.getTool('test');

      const result = await tool?.execute({});

      expect(result).toEqual({ success: true, output: 'executed test' });
    });
  });
});

describe('ToolRegistry 实际使用场景', () => {
  let registry: ToolRegistry;

  beforeEach(() => {
    registry = new ToolRegistry();
  });

  it('模拟工具发现流程', () => {
    // 注册一批工具，部分为核心工具，部分为延迟加载
    registry.registerAll([
      createMockToolWithMetadata('tool_search', {
        deferLoading: false,
        category: 'core',
        keywords: ['search', 'discover', 'find', 'tool'],
      }),
      createMockToolWithMetadata('read_file', {
        deferLoading: false,
        category: 'filesystem',
        keywords: ['read', 'file'],
      }),
      createMockToolWithMetadata('advanced_git', {
        deferLoading: true,
        category: 'git',
        keywords: ['git', 'version', 'control'],
      }),
      createMockToolWithMetadata('database_query', {
        deferLoading: true,
        category: 'database',
        keywords: ['database', 'sql', 'query'],
      }),
    ]);

    // 1. 获取核心工具（会话开始时）
    const coreTools = registry.getCoreTools();
    expect(coreTools).toHaveLength(2);
    expect(coreTools.map((t) => t.name)).toContain('tool_search');

    // 2. 搜索工具
    const gitResults = registry.search('git');
    expect(gitResults.some((r) => r.name === 'advanced_git')).toBe(true);

    // 3. 按需加载发现的工具
    const discoveredTools = registry.getTools(['advanced_git']);
    expect(discoveredTools).toHaveLength(1);
    expect(discoveredTools[0].name).toBe('advanced_git');
  });

  it('模拟多分类工具注册', () => {
    const categories: ToolCategory[] = ['core', 'filesystem', 'shell', 'git', 'web'];

    for (const category of categories) {
      registry.register(
        createMockToolWithMetadata(`${category}_tool`, {
          category,
          keywords: [category],
        })
      );
    }

    const metadata = registry.getAllMetadata();

    expect(metadata).toHaveLength(5);
    for (const category of categories) {
      expect(metadata.some((m) => m.category === category)).toBe(true);
    }
  });
});
