import {
  getModelGuidance,
  getToolBehaviorGuidance,
} from '../src/modules/netaclaw/runtime/prompt_guidance.js';
import {
  collectAvailableToolNames,
  buildSystemPrompt,
  BuildSystemPromptParams,
  buildLLMMessages,
} from '../src/modules/netaclaw/runtime/prompt_builder.js';

// ─── Mock Helper ───

function createMockSkillLoader(returnValue: string) {
  return { buildSkillsPrompt: jest.fn().mockReturnValue(returnValue) } as any;
}

// ─── collectAvailableToolNames ───

describe('collectAvailableToolNames', () => {
  it('基础工具始终包含', () => {
    const names = collectAvailableToolNames({});
    expect(names).toEqual(expect.arrayContaining(['bash', 'read_file', 'write_file', 'list_dir', 'todo']));
  });

  it('memoryEnabled 追加记忆工具', () => {
    const names = collectAvailableToolNames({ memoryEnabled: true });
    expect(names).toContain('memory_save');
    expect(names).toContain('memory_recall');
  });

  it('hasSkills 追加技能工具', () => {
    const names = collectAvailableToolNames({ hasSkills: true });
    expect(names).toContain('read_skill');
    expect(names).toContain('read_skill_file');
    expect(names).toContain('skill_manage');
  });

  it('crewRole=master 追加委派工具', () => {
    const names = collectAvailableToolNames({ crewRole: 'master' });
    expect(names).toContain('delegate_task');
    expect(names).toContain('delegate_parallel');
    expect(names).toContain('escalate');
  });

  it('crewRole=sub 不追加委派工具', () => {
    const names = collectAvailableToolNames({ crewRole: 'sub' });
    expect(names).not.toContain('delegate_task');
    expect(names).not.toContain('delegate_parallel');
    expect(names).not.toContain('escalate');
  });
});

// ─── getModelGuidance ───

describe('getModelGuidance', () => {
  it('Claude 返回空字符串', () => {
    expect(getModelGuidance('claude-3-opus')).toBe('');
    expect(getModelGuidance('Claude-Sonnet')).toBe('');
  });

  it('MiniMax 匹配 minimax 规则', () => {
    const g = getModelGuidance('minimax-abab6');
    expect(g).toContain('一个工具');
  });

  it('GPT 匹配 gpt 规则', () => {
    const g = getModelGuidance('gpt-4o');
    expect(g).toContain('tool_persistence');
  });

  it('DeepSeek 匹配 deepseek 规则', () => {
    const g = getModelGuidance('deepseek-chat');
    expect(g).toContain('并行');
  });

  it('doubao 匹配 doubao 规则', () => {
    const g = getModelGuidance('doubao-pro-32k');
    expect(g).toContain('non_interactive');
  });

  it('未知模型返回默认指导', () => {
    const g = getModelGuidance('some-unknown-model-xyz');
    expect(g).toContain('通用操作规范');
  });
});

// ─── getToolBehaviorGuidance ───

describe('getToolBehaviorGuidance', () => {
  it('包含 todo 时注入任务规划策略', () => {
    const g = getToolBehaviorGuidance(['todo', 'bash']);
    expect(g).toContain('任务规划策略');
  });

  it('包含 memory_save 时注入记忆策略', () => {
    const g = getToolBehaviorGuidance(['memory_save']);
    expect(g).toContain('记忆使用策略');
  });

  it('无匹配工具时返回空字符串', () => {
    const g = getToolBehaviorGuidance(['bash', 'read_file']);
    expect(g).toContain('命令执行策略');
    expect(g).toContain('文件读取策略');
  });
});

// ─── buildSystemPrompt ───

describe('buildSystemPrompt', () => {
  const baseParams: BuildSystemPromptParams = {
    agentSystemPrompt: '你是一个测试助手',
    modelId: 'gpt-4o',
    availableToolNames: ['bash', 'todo'],
    skills: [],
    skillLoader: createMockSkillLoader(''),
    memoryEnabled: false,
  };

  it('基础场景：包含 Agent 身份、工具纪律、模型指导、工具行为策略、元信息', () => {
    const { layers } = buildSystemPrompt(baseParams);
    const names = layers.map(l => l.name);
    expect(names).toContain('Agent 身份');
    expect(names).toContain('工具使用纪律');
    expect(names).toContain('模型特定指导');
    expect(names).toContain('工具行为策略');
    expect(names).toContain('元信息');
    expect(names).not.toContain('Skill 索引');
    expect(names).not.toContain('记忆系统');
    expect(names).not.toContain('Crew 编排上下文');
  });

  it('完整场景：skill + 记忆 + memoryContext → 包含所有层', () => {
    const loader = createMockSkillLoader('技能索引内容');
    const { layers } = buildSystemPrompt({
      ...baseParams,
      skills: ['skill_a'],
      skillLoader: loader,
      memoryEnabled: true,
      memoryContext: '用户偏好数据',
    });
    const names = layers.map(l => l.name);
    expect(names).toContain('Skill 索引');
    expect(names).toContain('记忆行为');
    expect(loader.buildSkillsPrompt).toHaveBeenCalled();
  });

  it('Crew 场景：注入 crewContext → 包含 Crew 编排上下文层', () => {
    const { layers } = buildSystemPrompt({
      ...baseParams,
      crewContext: '你是主Agent，负责分配任务',
    });
    const names = layers.map(l => l.name);
    expect(names).toContain('Crew 编排上下文');
    const crewLayer = layers.find(l => l.name === 'Crew 编排上下文');
    expect(crewLayer!.content).toContain('你是主Agent');
  });

  it('空 agentSystemPrompt 不生成 Agent 身份层', () => {
    const { layers } = buildSystemPrompt({ ...baseParams, agentSystemPrompt: '' });
    const names = layers.map(l => l.name);
    expect(names).not.toContain('Agent 身份');
  });

  it('Claude 模型不生成模型特定指导层', () => {
    const { layers } = buildSystemPrompt({ ...baseParams, modelId: 'claude-3-opus' });
    const names = layers.map(l => l.name);
    expect(names).not.toContain('模型特定指导');
  });
});

describe('buildLLMMessages', () => {
  it('视频附件有旧伤检测 skill 时提示使用 execute_skill 而不是 image_recognize', () => {
    const messages = buildLLMMessages(
      '<skill name="vehicle-damage-inspection" type="compute-entry">汽车环车视频旧伤检测</skill>',
      [],
      {
        content: '请分析',
        metadata: {
          attachments: [{
            type: 'video',
            name: 'car.mp4',
            url: '/upload/20260507/car.mp4',
          }],
        },
      },
      ['execute_skill', 'image_recognize'],
    );
    const hint = messages[messages.length - 1].content;
    expect(hint).toContain('execute_skill');
    expect(hint).toContain('vehicle-damage-inspection');
    expect(hint).toContain('/upload/20260507/car.mp4');
    expect(hint).toContain('不要用它直接分析视频文件');
  });
});
