/**
 * MCP 工具适配器测试
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MCPToolAdapter, createMCPToolAdapter } from '../../../src/mcp/tool-adapter.js';
import type { MCPTool, MCPManager, MCPToolCallResult } from '../../../src/mcp/types.js';

// 创建 mock MCPManager
function createMockManager(): MCPManager {
  return {
    callTool: vi.fn(),
    getTools: vi.fn().mockReturnValue([]),
    getServerStatuses: vi.fn().mockReturnValue([]),
    initialize: vi.fn(),
    shutdown: vi.fn(),
    on: vi.fn(),
    emit: vi.fn(),
  } as unknown as MCPManager;
}

describe('MCPToolAdapter', () => {
  let mockManager: MCPManager;
  let adapter: MCPToolAdapter;

  beforeEach(() => {
    mockManager = createMockManager();
    adapter = new MCPToolAdapter(mockManager);
  });

  describe('adaptToInternalTool', () => {
    it('应该将 MCP 工具转换为内部格式', () => {
      const mcpTool: MCPTool = {
        server: 'filesystem',
        name: 'filesystem-read_file',
        originalName: 'read_file',
        description: 'Read a file from the filesystem',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path to the file',
            },
          },
          required: ['path'],
        },
      };

      const internalTool = adapter.adaptToInternalTool(mcpTool);

      expect(internalTool.name).toBe('filesystem-read_file');
      expect(internalTool.description).toBe('Read a file from the filesystem');
      expect(internalTool.parameters).toHaveProperty('path');
      expect(internalTool.parameters.path.type).toBe('string');
      expect(internalTool.parameters.path.required).toBe(true);
      expect(internalTool.metadata.category).toBe('agent');
      expect(internalTool.metadata.keywords).toContain('mcp');
      expect(internalTool.metadata.keywords).toContain('filesystem');
    });

    it('应该正确转换参数类型', () => {
      const mcpTool: MCPTool = {
        server: 'test',
        name: 'test-tool',
        originalName: 'tool',
        description: 'Test tool',
        inputSchema: {
          type: 'object',
          properties: {
            stringParam: { type: 'string' },
            numberParam: { type: 'number' },
            integerParam: { type: 'integer' },
            booleanParam: { type: 'boolean' },
            arrayParam: { type: 'array' },
            objectParam: { type: 'object' },
          },
        },
      };

      const internalTool = adapter.adaptToInternalTool(mcpTool);

      expect(internalTool.parameters.stringParam.type).toBe('string');
      expect(internalTool.parameters.numberParam.type).toBe('number');
      expect(internalTool.parameters.integerParam.type).toBe('number');
      expect(internalTool.parameters.booleanParam.type).toBe('boolean');
      expect(internalTool.parameters.arrayParam.type).toBe('array');
      expect(internalTool.parameters.objectParam.type).toBe('object');
    });

    it('应该处理联合类型', () => {
      const mcpTool: MCPTool = {
        server: 'test',
        name: 'test-tool',
        originalName: 'tool',
        description: 'Test tool',
        inputSchema: {
          type: 'object',
          properties: {
            nullable: { type: ['string', 'null'] },
          },
        },
      };

      const internalTool = adapter.adaptToInternalTool(mcpTool);
      expect(internalTool.parameters.nullable.type).toBe('string');
    });

    it('应该处理空 inputSchema', () => {
      const mcpTool: MCPTool = {
        server: 'test',
        name: 'test-tool',
        originalName: 'tool',
        description: 'Test tool',
        inputSchema: {},
      };

      const internalTool = adapter.adaptToInternalTool(mcpTool);
      expect(internalTool.parameters).toEqual({});
    });

    it('execute 应该调用 manager.callTool', async () => {
      const mcpTool: MCPTool = {
        server: 'test',
        name: 'test-tool',
        originalName: 'tool',
        description: 'Test tool',
        inputSchema: {
          type: 'object',
          properties: {
            arg: { type: 'string' },
          },
        },
      };

      const mockResult: MCPToolCallResult = {
        success: true,
        content: [{ type: 'text', text: 'result' }],
        isError: false,
      };
      vi.mocked(mockManager.callTool).mockResolvedValue(mockResult);

      const internalTool = adapter.adaptToInternalTool(mcpTool);
      const result = await internalTool.execute({ arg: 'value' });

      expect(mockManager.callTool).toHaveBeenCalledWith('test-tool', {
        arg: 'value',
      });
      expect(result.success).toBe(true);
      expect(result.output).toBe('result');
    });

    it('execute 应该处理错误结果', async () => {
      const mcpTool: MCPTool = {
        server: 'test',
        name: 'test-tool',
        originalName: 'tool',
        description: 'Test tool',
        inputSchema: {},
      };

      const mockResult: MCPToolCallResult = {
        success: false,
        content: [{ type: 'text', text: 'error message' }],
        isError: true,
      };
      vi.mocked(mockManager.callTool).mockResolvedValue(mockResult);

      const internalTool = adapter.adaptToInternalTool(mcpTool);
      const result = await internalTool.execute({});

      expect(result.success).toBe(false);
      expect(result.error).toBe('error message');
    });
  });

  describe('adaptTools', () => {
    it('应该批量转换工具', () => {
      const mcpTools: MCPTool[] = [
        {
          server: 'server1',
          name: 'server1-tool1',
          originalName: 'tool1',
          description: 'Tool 1',
          inputSchema: {},
        },
        {
          server: 'server2',
          name: 'server2-tool2',
          originalName: 'tool2',
          description: 'Tool 2',
          inputSchema: {},
        },
      ];

      const internalTools = adapter.adaptTools(mcpTools);

      expect(internalTools).toHaveLength(2);
      expect(internalTools[0].name).toBe('server1-tool1');
      expect(internalTools[1].name).toBe('server2-tool2');
    });
  });

  describe('createMCPToolAdapter', () => {
    it('应该创建适配器实例', () => {
      const adapter = createMCPToolAdapter(mockManager);
      expect(adapter).toBeInstanceOf(MCPToolAdapter);
    });
  });

  describe('内容转换', () => {
    it('应该处理图片内容', async () => {
      const mcpTool: MCPTool = {
        server: 'test',
        name: 'test-tool',
        originalName: 'tool',
        description: 'Test tool',
        inputSchema: {},
      };

      const mockResult: MCPToolCallResult = {
        success: true,
        content: [{ type: 'image', data: 'base64data', mimeType: 'image/png' }],
        isError: false,
      };
      vi.mocked(mockManager.callTool).mockResolvedValue(mockResult);

      const internalTool = adapter.adaptToInternalTool(mcpTool);
      const result = await internalTool.execute({});

      expect(result.output).toBe('[Image: image/png]');
    });

    it('应该处理资源内容', async () => {
      const mcpTool: MCPTool = {
        server: 'test',
        name: 'test-tool',
        originalName: 'tool',
        description: 'Test tool',
        inputSchema: {},
      };

      const mockResult: MCPToolCallResult = {
        success: true,
        content: [
          {
            type: 'resource',
            resource: {
              uri: 'file:///test.txt',
              text: 'file content',
            },
          },
        ],
        isError: false,
      };
      vi.mocked(mockManager.callTool).mockResolvedValue(mockResult);

      const internalTool = adapter.adaptToInternalTool(mcpTool);
      const result = await internalTool.execute({});

      expect(result.output).toBe('file content');
    });

    it('应该处理混合内容', async () => {
      const mcpTool: MCPTool = {
        server: 'test',
        name: 'test-tool',
        originalName: 'tool',
        description: 'Test tool',
        inputSchema: {},
      };

      const mockResult: MCPToolCallResult = {
        success: true,
        content: [
          { type: 'text', text: 'line 1' },
          { type: 'text', text: 'line 2' },
        ],
        isError: false,
      };
      vi.mocked(mockManager.callTool).mockResolvedValue(mockResult);

      const internalTool = adapter.adaptToInternalTool(mcpTool);
      const result = await internalTool.execute({});

      expect(result.output).toBe('line 1\nline 2');
    });
  });
});
