import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
  SkillRegistry,
  getSkillRegistry,
  resetSkillRegistry,
} from '../../../src/skills/registry.js';
import type { Skill } from '../../../src/skills/types.js';

// Mock loader to prevent file system access
vi.mock('../../../src/skills/loader.js', () => ({
  skillLoader: {
    loadFromDirectory: vi.fn().mockResolvedValue([]),
    getUserSkillsDir: vi.fn().mockReturnValue('/mock/user/skills'),
    getProjectSkillsDir: vi.fn().mockReturnValue('/mock/project/skills'),
  },
}));

// Mock builtin skills
vi.mock('../../../src/skills/builtin/index.js', () => ({
  builtinSkills: [
    {
      name: 'test-builtin',
      description: '内置测试 Skill',
      promptTemplate: '测试提示: {{param1}}',
      parameters: {
        param1: { type: 'string', required: true, description: '参数1' },
      },
      source: 'builtin',
      enabled: true,
    },
  ],
}));

describe('SkillRegistry - Skill 注册表', () => {
  let registry: SkillRegistry;

  beforeEach(() => {
    resetSkillRegistry();
    registry = new SkillRegistry({ autoLoad: false });
  });

  describe('register - 注册', () => {
    it('成功注册 Skill', () => {
      const skill: Skill = {
        name: 'test-skill',
        description: '测试 Skill',
        promptTemplate: '测试提示',
        source: 'user',
      };

      registry.register(skill);
      expect(registry.has('test-skill')).toBe(true);
    });

    it('高优先级 Skill 可以覆盖低优先级', () => {
      const builtinSkill: Skill = {
        name: 'same-name',
        description: '内置版本',
        promptTemplate: '内置提示',
        source: 'builtin',
      };

      const projectSkill: Skill = {
        name: 'same-name',
        description: '项目版本',
        promptTemplate: '项目提示',
        source: 'project',
      };

      registry.register(builtinSkill);
      registry.register(projectSkill);

      const result = registry.get('same-name');
      expect(result?.source).toBe('project');
    });

    it('低优先级 Skill 不能覆盖高优先级', () => {
      const projectSkill: Skill = {
        name: 'same-name',
        description: '项目版本',
        promptTemplate: '项目提示',
        source: 'project',
      };

      const builtinSkill: Skill = {
        name: 'same-name',
        description: '内置版本',
        promptTemplate: '内置提示',
        source: 'builtin',
      };

      registry.register(projectSkill);
      registry.register(builtinSkill);

      const result = registry.get('same-name');
      expect(result?.source).toBe('project');
    });
  });

  describe('get - 获取', () => {
    it('获取存在的 Skill', () => {
      const skill: Skill = {
        name: 'test-skill',
        description: '测试',
        promptTemplate: '提示',
        source: 'user',
      };

      registry.register(skill);
      const result = registry.get('test-skill');

      expect(result).toBeDefined();
      expect(result?.name).toBe('test-skill');
    });

    it('获取不存在的 Skill 返回 undefined', () => {
      const result = registry.get('non-existent');
      expect(result).toBeUndefined();
    });
  });

  describe('search - 搜索', () => {
    beforeEach(() => {
      const skills: Skill[] = [
        {
          name: 'code-review',
          description: '代码审查',
          promptTemplate: '审查代码',
          keywords: ['review', 'quality'],
          category: 'development',
          source: 'builtin',
          enabled: true,
        },
        {
          name: 'generate-tests',
          description: '生成测试',
          promptTemplate: '生成测试代码',
          keywords: ['test', 'unit'],
          category: 'testing',
          source: 'builtin',
          enabled: true,
        },
        {
          name: 'explain-code',
          description: '解释代码功能',
          promptTemplate: '解释代码',
          category: 'development',
          source: 'builtin',
          enabled: true,
        },
      ];

      for (const skill of skills) {
        registry.register(skill);
      }
    });

    it('按名称精确匹配', () => {
      const results = registry.search('code-review');
      expect(results.length).toBeGreaterThan(0);
      expect(results[0].skill.name).toBe('code-review');
      expect(results[0].score).toBe(100);
    });

    it('按名称前缀匹配', () => {
      const results = registry.search('code');
      expect(results.length).toBe(2); // code-review, explain-code
    });

    it('按描述匹配', () => {
      const results = registry.search('审查');
      expect(results.length).toBeGreaterThan(0);
      expect(results[0].skill.name).toBe('code-review');
    });

    it('按关键词匹配', () => {
      const results = registry.search('quality');
      expect(results.length).toBeGreaterThan(0);
      expect(results[0].skill.name).toBe('code-review');
    });

    it('按分类匹配', () => {
      const results = registry.search('testing');
      expect(results.length).toBeGreaterThan(0);
      expect(results[0].skill.name).toBe('generate-tests');
    });

    it('限制结果数量', () => {
      const results = registry.search('code', 1);
      expect(results.length).toBe(1);
    });
  });

  describe('renderPrompt - 渲染模板', () => {
    it('成功渲染模板', () => {
      const skill: Skill = {
        name: 'test',
        description: '测试',
        promptTemplate: '你好 {{name}}，请{{action}}',
        source: 'user',
      };

      const result = registry.renderPrompt(skill, {
        name: '世界',
        action: '测试',
      });

      expect(result.success).toBe(true);
      expect(result.prompt).toBe('你好 世界，请测试');
    });

    it('缺少必需参数时返回错误', () => {
      const skill: Skill = {
        name: 'test',
        description: '测试',
        promptTemplate: '{{param}}',
        parameters: {
          param: { type: 'string', required: true, description: '必需参数' },
        },
        source: 'user',
      };

      const result = registry.renderPrompt(skill, {});

      expect(result.success).toBe(false);
      expect(result.error).toContain('缺少必需参数');
    });

    it('使用默认值', () => {
      const skill: Skill = {
        name: 'test',
        description: '测试',
        promptTemplate: '值: {{param}}',
        parameters: {
          param: {
            type: 'string',
            required: false,
            default: '默认值',
            description: '可选参数',
          },
        },
        source: 'user',
      };

      const result = registry.renderPrompt(skill, {});

      expect(result.success).toBe(true);
      expect(result.prompt).toBe('值: 默认值');
    });

    it('保留未匹配的变量', () => {
      const skill: Skill = {
        name: 'test',
        description: '测试',
        promptTemplate: '{{known}} 和 {{unknown}}',
        source: 'user',
      };

      const result = registry.renderPrompt(skill, { known: '已知' });

      expect(result.success).toBe(true);
      expect(result.prompt).toBe('已知 和 {{unknown}}');
    });
  });

  describe('execute - 执行', () => {
    it('成功执行 Skill', () => {
      const skill: Skill = {
        name: 'test',
        description: '测试',
        promptTemplate: '提示: {{param}}',
        source: 'user',
        enabled: true,
      };

      registry.register(skill);
      const result = registry.execute('test', { param: '值' });

      expect(result.success).toBe(true);
      expect(result.prompt).toBe('提示: 值');
    });

    it('Skill 不存在时返回错误', () => {
      const result = registry.execute('non-existent', {});

      expect(result.success).toBe(false);
      expect(result.error).toContain('不存在');
    });

    it('Skill 禁用时返回错误', () => {
      const skill: Skill = {
        name: 'disabled',
        description: '禁用的 Skill',
        promptTemplate: '提示',
        source: 'user',
        enabled: false,
      };

      registry.register(skill);
      const result = registry.execute('disabled', {});

      expect(result.success).toBe(false);
      expect(result.error).toContain('禁用');
    });
  });

  describe('getByCategory - 按分类获取', () => {
    it('返回指定分类的 Skills', () => {
      const skills: Skill[] = [
        {
          name: 'skill1',
          description: '1',
          promptTemplate: '1',
          category: 'dev',
          source: 'builtin',
          enabled: true,
        },
        {
          name: 'skill2',
          description: '2',
          promptTemplate: '2',
          category: 'dev',
          source: 'builtin',
          enabled: true,
        },
        {
          name: 'skill3',
          description: '3',
          promptTemplate: '3',
          category: 'test',
          source: 'builtin',
          enabled: true,
        },
      ];

      for (const skill of skills) {
        registry.register(skill);
      }

      const devSkills = registry.getByCategory('dev');
      expect(devSkills.length).toBe(2);
    });
  });

  describe('getStats - 统计信息', () => {
    it('返回正确的统计信息', () => {
      const skills: Skill[] = [
        {
          name: 'builtin1',
          description: '1',
          promptTemplate: '1',
          category: 'dev',
          source: 'builtin',
          enabled: true,
        },
        {
          name: 'user1',
          description: '2',
          promptTemplate: '2',
          category: 'dev',
          source: 'user',
          enabled: true,
        },
        {
          name: 'disabled1',
          description: '3',
          promptTemplate: '3',
          source: 'builtin',
          enabled: false,
        },
      ];

      for (const skill of skills) {
        registry.register(skill);
      }

      const stats = registry.getStats();

      expect(stats.total).toBe(3);
      expect(stats.enabled).toBe(2);
      expect(stats.bySource.builtin).toBe(2);
      expect(stats.bySource.user).toBe(1);
      expect(stats.byCategory.dev).toBe(2);
    });
  });
});

describe('getSkillRegistry - 全局注册表', () => {
  beforeEach(() => {
    resetSkillRegistry();
  });

  it('返回单例实例', () => {
    const registry1 = getSkillRegistry();
    const registry2 = getSkillRegistry();

    expect(registry1).toBe(registry2);
  });

  it('resetSkillRegistry 重置实例', () => {
    const registry1 = getSkillRegistry();
    resetSkillRegistry();
    const registry2 = getSkillRegistry();

    expect(registry1).not.toBe(registry2);
  });
});
