import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { Agent } from '../../../src/core/agent.js';
import { ToolRegistry } from '../../../src/tools/registry.js';
import { SessionManager } from '../../../src/session/index.js';
import type { AgentConfig, Tool } from '../../../src/types/index.js';
import type { AgentInfo } from '../../../src/agent/types.js';

// Mock providers
vi.mock('../../../src/core/providers.js', () => ({
  getModelFactory: vi.fn(() => {
    return (model: string) => ({
      modelId: `mock:${model}`,
      doGenerate: vi.fn(),
      doStream: vi.fn(),
    });
  }),
}));

// Mock AI SDK
vi.mock('ai', () => ({
  generateText: vi.fn().mockResolvedValue({
    text: 'Mock response',
    response: { messages: [] },
    steps: [],
  }),
  streamText: vi.fn(() => ({
    textStream: (async function* () {
      yield 'Mock ';
      yield 'streamed ';
      yield 'response';
    })(),
    response: Promise.resolve({ messages: [] }),
  })),
  stepCountIs: vi.fn(() => () => false),
}));

// Mock agent registry and executor
vi.mock('../../../src/agent/index.js', () => ({
  agentRegistry: {
    get: vi.fn(),
    getInternal: vi.fn(() => null), // Returns null by default - no summary agent configured
  },
  AgentExecutor: vi.fn().mockImplementation(() => ({
    execute: vi.fn().mockResolvedValue({
      success: true,
      text: 'Vision analysis result',
      steps: 1,
      sessionId: 'test',
    }),
  })),
}));

// Mock vision and summary config
vi.mock('../../../src/utils/config.js', () => ({
  loadVisionConfig: vi.fn(() => null),
  loadSummaryConfig: vi.fn(() => null),
}));

// Create mock tool
function createMockTool(name: string, category = 'core'): Tool & { metadata?: { deferLoading?: boolean } } {
  return {
    name,
    description: `Mock ${name} tool`,
    parameters: { type: 'object', properties: {}, required: [] },
    execute: vi.fn().mockResolvedValue({ success: true, output: `${name} result` }),
    metadata: { deferLoading: category !== 'core' },
  };
}

// Default test config
const testConfig: AgentConfig = {
  provider: 'anthropic',
  apiKey: 'test-api-key',
  model: 'claude-3-sonnet',
  systemPrompt: 'You are a helpful assistant.',
  maxTokens: 4096,
};

describe('Agent', () => {
  let agent: Agent;
  let registry: ToolRegistry;

  beforeEach(() => {
    vi.clearAllMocks();
    registry = new ToolRegistry();

    // Register some tools
    registry.register(createMockTool('tool_search', 'core') as any);
    registry.register(createMockTool('bash', 'core') as any);
    registry.register(createMockTool('read_file', 'filesystem') as any);
    registry.register(createMockTool('write_file', 'filesystem') as any);

    agent = new Agent(testConfig);
    agent.setRegistry(registry);
  });

  describe('constructor', () => {
    it('初始化 Agent 配置', () => {
      const config = agent.getConfig();
      expect(config.provider).toBe('anthropic');
      expect(config.model).toBe('claude-3-sonnet');
      expect(config.systemPrompt).toBe('You are a helpful assistant.');
    });

    it('支持自定义压缩配置', () => {
      const agentWithCompression = new Agent(testConfig, {
        maxContextTokens: 50000,
        compressionThreshold: 0.7,
      });

      expect(agentWithCompression.getCompressionManager()).toBeDefined();
    });
  });

  describe('setRegistry', () => {
    it('设置工具注册表', () => {
      const newAgent = new Agent(testConfig);
      expect(newAgent.getToolCount()).toEqual({ core: 0, discovered: 0, total: 0 });

      newAgent.setRegistry(registry);
      const counts = newAgent.getToolCount();
      expect(counts.core).toBeGreaterThan(0);
    });
  });

  describe('setSessionManager', () => {
    it('设置会话管理器', async () => {
      // Mock SessionManager
      const mockSessionManager = {
        getSession: vi.fn().mockReturnValue(null),
        setMessages: vi.fn(),
        setDiscoveredTools: vi.fn(),
        newSession: vi.fn(),
      } as unknown as SessionManager;

      agent.setSessionManager(mockSessionManager);
      expect(agent.getSessionManager()).toBe(mockSessionManager);
    });

    it('从会话恢复状态', async () => {
      // Mock SessionManager with existing session
      const mockSessionManager = {
        getSession: vi.fn().mockReturnValue({
          id: 'test-session',
          messages: [{ role: 'user', content: 'Hello' }],
          discoveredTools: ['read_file', 'write_file'],
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
        }),
        setMessages: vi.fn(),
        setDiscoveredTools: vi.fn(),
        newSession: vi.fn(),
      } as unknown as SessionManager;

      agent.setSessionManager(mockSessionManager);

      const counts = agent.getToolCount();
      expect(counts.discovered).toBe(2);
    });
  });

  describe('getToolCount', () => {
    it('返回工具数量统计', () => {
      const counts = agent.getToolCount();
      expect(counts).toHaveProperty('core');
      expect(counts).toHaveProperty('discovered');
      expect(counts).toHaveProperty('total');
      expect(counts.total).toBe(counts.core + counts.discovered);
    });

    it('registry 未设置时返回 0', () => {
      const newAgent = new Agent(testConfig);
      const counts = newAgent.getToolCount();
      expect(counts).toEqual({ core: 0, discovered: 0, total: 0 });
    });
  });

  describe('getHistory', () => {
    it('初始历史为空', () => {
      expect(agent.getHistory()).toEqual([]);
    });
  });

  describe('clearHistory', () => {
    it('清空对话历史和发现的工具', async () => {
      // Add some history by calling chat (mocked)
      await agent.chat('Hello');

      await agent.clearHistory();
      expect(agent.getHistory()).toEqual([]);
      expect(agent.getToolCount().discovered).toBe(0);
    });

    it('有会话管理器时创建新会话', async () => {
      const mockSessionManager = {
        getSession: vi.fn().mockReturnValue(null),
        setMessages: vi.fn(),
        setDiscoveredTools: vi.fn(),
        newSession: vi.fn().mockResolvedValue(undefined),
      } as unknown as SessionManager;

      agent.setSessionManager(mockSessionManager);
      await agent.clearHistory();

      expect(mockSessionManager.newSession).toHaveBeenCalled();
    });
  });

  describe('Agent Mode', () => {
    const exploreAgent: AgentInfo = {
      name: 'explore',
      description: '代码探索 Agent',
      mode: 'subagent',
      prompt: 'You are an explore agent.',
      tools: {
        enabled: ['read_file', 'tool_search'],
        noTask: true,
      },
    };

    it('设置 Agent 模式', () => {
      agent.setAgentMode(exploreAgent);
      expect(agent.getAgentMode()).toBe(exploreAgent);
      expect(agent.getAgentModeName()).toBe('explore');
    });

    it('切换 Agent 时更新 system prompt', () => {
      agent.setAgentMode(exploreAgent);
      expect(agent.getConfig().systemPrompt).toBe('You are an explore agent.');
    });

    it('切换回 default 时恢复原始 prompt', () => {
      agent.setAgentMode(exploreAgent);
      agent.setAgentMode(null);

      expect(agent.getAgentModeName()).toBe('default');
      expect(agent.getConfig().systemPrompt).toBe('You are a helpful assistant.');
    });

    it('Agent 没有自定义 prompt 时保持原始 prompt', () => {
      const agentWithoutPrompt: AgentInfo = {
        name: 'simple',
        description: 'Simple agent',
        mode: 'subagent',
      };

      agent.setAgentMode(agentWithoutPrompt);
      expect(agent.getConfig().systemPrompt).toBe('You are a helpful assistant.');
    });
  });

  describe('supportsVision', () => {
    it('Anthropic Claude-3 支持 vision', () => {
      const claudeAgent = new Agent({
        ...testConfig,
        provider: 'anthropic',
        model: 'claude-3-opus',
      });
      expect(claudeAgent.supportsVision()).toBe(true);
    });

    it('Anthropic Claude-4 支持 vision', () => {
      const claudeAgent = new Agent({
        ...testConfig,
        provider: 'anthropic',
        model: 'claude-4-sonnet',
      });
      expect(claudeAgent.supportsVision()).toBe(true);
    });

    it('OpenAI GPT-4 支持 vision', () => {
      const gptAgent = new Agent({
        ...testConfig,
        provider: 'openai',
        model: 'gpt-4-turbo',
      });
      expect(gptAgent.supportsVision()).toBe(true);
    });

    it('DeepSeek 不支持 vision', () => {
      const deepseekAgent = new Agent({
        ...testConfig,
        provider: 'deepseek',
        model: 'deepseek-chat',
      });
      expect(deepseekAgent.supportsVision()).toBe(false);
    });

    it('旧版 Claude 不支持 vision', () => {
      const oldClaudeAgent = new Agent({
        ...testConfig,
        provider: 'anthropic',
        model: 'claude-2',
      });
      expect(oldClaudeAgent.supportsVision()).toBe(false);
    });

    it('GPT-3.5 不支持 vision', () => {
      const gpt35Agent = new Agent({
        ...testConfig,
        provider: 'openai',
        model: 'gpt-3.5-turbo',
      });
      expect(gpt35Agent.supportsVision()).toBe(false);
    });
  });

  describe('getContextUsage', () => {
    it('返回 token 使用情况', () => {
      const usage = agent.getContextUsage();
      expect(usage).toHaveProperty('input');
      expect(usage).toHaveProperty('contextLimit');
      expect(usage).toHaveProperty('available');
      expect(usage).toHaveProperty('usagePercent');
    });
  });

  describe('getContextUsageFormatted', () => {
    it('返回格式化的使用情况字符串', () => {
      const formatted = agent.getContextUsageFormatted();
      expect(typeof formatted).toBe('string');
      expect(formatted).toContain('k');
    });
  });

  describe('getCompressionManager', () => {
    it('返回压缩管理器实例', () => {
      const manager = agent.getCompressionManager();
      expect(manager).toBeDefined();
      expect(manager).toHaveProperty('compress');
      expect(manager).toHaveProperty('forceCompress');
    });
  });

  describe('compactHistory', () => {
    it('手动压缩对话历史', async () => {
      const result = await agent.compactHistory();
      expect(result).toHaveProperty('freedTokens');
      expect(result).toHaveProperty('type');
    });
  });

  describe('getConfig', () => {
    it('返回配置副本', () => {
      const config = agent.getConfig();
      expect(config).toEqual(testConfig);

      // 修改返回值不影响原始配置
      config.model = 'modified';
      expect(agent.getConfig().model).toBe('claude-3-sonnet');
    });
  });
});

describe('Agent - getAvailableTools error handling', () => {
  it('registry 未设置时抛出错误', () => {
    const agent = new Agent(testConfig);

    // 使用 getToolCount 间接测试（因为 getAvailableTools 是 private）
    // 当 registry 为 null 时，getToolCount 返回 0
    const counts = agent.getToolCount();
    expect(counts.total).toBe(0);
  });
});

describe('Agent - chat with images', () => {
  let agent: Agent;
  let registry: ToolRegistry;

  beforeEach(() => {
    vi.clearAllMocks();
    registry = new ToolRegistry();
    registry.register(createMockTool('tool_search', 'core') as any);

    agent = new Agent(testConfig);
    agent.setRegistry(registry);
  });

  it('支持 vision 的模型直接处理图片', async () => {
    const visionAgent = new Agent({
      ...testConfig,
      model: 'claude-3-opus',
    });
    visionAgent.setRegistry(registry);

    const result = await visionAgent.chat({
      text: 'What is in this image?',
      images: [
        { data: 'base64data', mimeType: 'image/png' },
      ],
    });

    expect(result).toBeDefined();
    expect(result.text).toBeDefined();
  });

  it('不支持 vision 时返回错误消息（Vision 未配置）', async () => {
    const deepseekAgent = new Agent({
      ...testConfig,
      provider: 'deepseek',
      model: 'deepseek-chat',
    });
    deepseekAgent.setRegistry(registry);

    const result = await deepseekAgent.chat({
      text: 'What is in this image?',
      images: [
        { data: 'base64data', mimeType: 'image/png' },
      ],
    });

    expect(result.text).toContain('无法处理图片');
  });
});
