import { describe, it, expect, beforeEach, vi } from 'vitest';
import { SkillLoader } from '../../../src/skills/loader.js';

// Mock fs/promises
vi.mock('fs/promises', () => ({
  access: vi.fn(),
  readdir: vi.fn(),
  readFile: vi.fn(),
}));

import * as fs from 'fs/promises';

describe('SkillLoader - Skill 加载器', () => {
  let loader: SkillLoader;

  beforeEach(() => {
    vi.clearAllMocks();
    loader = new SkillLoader();
  });

  describe('loadFromFile - 从文件加载', () => {
    it('加载 YAML 格式的 Skill', async () => {
      const yamlContent = `
skill:
  name: test-skill
  description: 测试 Skill
  promptTemplate: "提示模板 {{param}}"
  parameters:
    param:
      type: string
      required: true
      description: 参数描述
`;

      vi.mocked(fs.readFile).mockResolvedValue(yamlContent);

      const skill = await loader.loadFromFile('/test/skill.yaml', 'user');

      expect(skill).not.toBeNull();
      expect(skill?.name).toBe('test-skill');
      expect(skill?.description).toBe('测试 Skill');
      expect(skill?.source).toBe('user');
      expect(skill?.parameters?.param.required).toBe(true);
    });

    it('加载 JSON 格式的 Skill', async () => {
      const jsonContent = JSON.stringify({
        skill: {
          name: 'json-skill',
          description: 'JSON Skill',
          promptTemplate: '提示',
        },
      });

      vi.mocked(fs.readFile).mockResolvedValue(jsonContent);

      const skill = await loader.loadFromFile('/test/skill.json', 'project');

      expect(skill).not.toBeNull();
      expect(skill?.name).toBe('json-skill');
      expect(skill?.source).toBe('project');
    });

    it('加载 Markdown 格式的 Skill（带 frontmatter）', async () => {
      const mdContent = `---
name: md-skill
description: Markdown Skill
parameters:
  code:
    type: string
    required: true
    description: 代码
---

# 代码审查

请审查以下代码：

{{code}}
`;

      vi.mocked(fs.readFile).mockResolvedValue(mdContent);

      const skill = await loader.loadFromFile('/test/skill.md', 'user');

      expect(skill).not.toBeNull();
      expect(skill?.name).toBe('md-skill');
      expect(skill?.description).toBe('Markdown Skill');
      expect(skill?.promptTemplate).toContain('{{code}}');
      expect(skill?.promptTemplate).toContain('代码审查');
    });

    it('加载 Markdown 格式的 Skill（无 frontmatter）', async () => {
      const mdContent = `# 简单提示

这是一个简单的提示模板。

{{input}}
`;

      vi.mocked(fs.readFile).mockResolvedValue(mdContent);

      const skill = await loader.loadFromFile('/test/simple.md', 'user');

      expect(skill).not.toBeNull();
      expect(skill?.name).toBe('simple'); // 从文件名获取
      expect(skill?.promptTemplate).toContain('简单提示');
    });

    it('缺少必需字段时返回 null', async () => {
      const yamlContent = `
skill:
  description: 没有 name 和 promptTemplate
`;

      vi.mocked(fs.readFile).mockResolvedValue(yamlContent);

      const skill = await loader.loadFromFile('/test/invalid.yaml', 'user');

      expect(skill).toBeNull();
    });
  });

  describe('loadFromDirectory - 从目录加载', () => {
    it('目录不存在时返回空数组', async () => {
      vi.mocked(fs.access).mockRejectedValue(new Error('Not found'));

      const skills = await loader.loadFromDirectory('/non-existent', 'user');

      expect(skills).toEqual([]);
    });

    it('加载目录中的所有 Skill 文件', async () => {
      vi.mocked(fs.access).mockResolvedValue(undefined);
      vi.mocked(fs.readdir).mockResolvedValue([
        { name: 'skill1.yaml', isFile: () => true, isDirectory: () => false },
        { name: 'skill2.json', isFile: () => true, isDirectory: () => false },
        { name: 'readme.txt', isFile: () => true, isDirectory: () => false }, // 不支持的格式
      ] as any);

      vi.mocked(fs.readFile)
        .mockResolvedValueOnce(`skill:\n  name: skill1\n  description: 1\n  promptTemplate: t1`)
        .mockResolvedValueOnce(JSON.stringify({ skill: { name: 'skill2', description: '2', promptTemplate: 't2' } }));

      const skills = await loader.loadFromDirectory('/test', 'user');

      expect(skills.length).toBe(2);
      expect(skills.map((s) => s.name)).toContain('skill1');
      expect(skills.map((s) => s.name)).toContain('skill2');
    });

    it('递归加载子目录', async () => {
      vi.mocked(fs.access).mockResolvedValue(undefined);
      vi.mocked(fs.readdir)
        .mockResolvedValueOnce([
          { name: 'subdir', isFile: () => false, isDirectory: () => true },
          { name: 'root.yaml', isFile: () => true, isDirectory: () => false },
        ] as any)
        .mockResolvedValueOnce([
          { name: 'sub.yaml', isFile: () => true, isDirectory: () => false },
        ] as any);

      vi.mocked(fs.readFile)
        .mockResolvedValueOnce(`skill:\n  name: root\n  description: root\n  promptTemplate: root`)
        .mockResolvedValueOnce(`skill:\n  name: sub\n  description: sub\n  promptTemplate: sub`);

      const skills = await loader.loadFromDirectory('/test', 'user');

      expect(skills.length).toBe(2);
      expect(skills.map((s) => s.name)).toContain('root');
      expect(skills.map((s) => s.name)).toContain('sub');
    });
  });

  describe('目录路径', () => {
    it('getUserSkillsDir 返回用户 Skills 目录', () => {
      const originalHome = process.env.HOME;
      process.env.HOME = '/home/testuser';

      const dir = loader.getUserSkillsDir();

      expect(dir).toBe('/home/testuser/.config/ai-terminal/skills');

      process.env.HOME = originalHome;
    });

    it('getProjectSkillsDir 返回项目 Skills 目录', () => {
      const dir = loader.getProjectSkillsDir('/workspace');

      expect(dir).toBe('/workspace/.ai-terminal/skills');
    });
  });
});
