import { describe, it, expect, beforeEach, vi } from 'vitest';
import { CommandLoader } from '../../../src/commands/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('CommandLoader - Command 加载器', () => {
  let loader: CommandLoader;

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

  describe('parseMarkdownCommand - 解析 Markdown', () => {
    it('解析带 frontmatter 的 Markdown', () => {
      const content = `---
description: 代码审查
agent: explore
model: sonnet
subtask: true
---

You are a code reviewer.

Input: $ARGUMENTS
`;

      const command = loader.parseMarkdownCommand(content, 'review', 'user');

      expect(command).not.toBeNull();
      expect(command?.name).toBe('review');
      expect(command?.description).toBe('代码审查');
      expect(command?.agent).toBe('explore');
      expect(command?.model).toBe('sonnet');
      expect(command?.subtask).toBe(true);
      expect(command?.template).toContain('You are a code reviewer');
      expect(command?.template).toContain('$ARGUMENTS');
      expect(command?.source).toBe('user');
    });

    it('解析不带 frontmatter 的 Markdown', () => {
      const content = `# Simple Command

This is a simple prompt template.

$ARGUMENTS
`;

      const command = loader.parseMarkdownCommand(content, 'simple', 'project');

      expect(command).not.toBeNull();
      expect(command?.name).toBe('simple');
      expect(command?.description).toBeUndefined();
      expect(command?.template).toContain('Simple Command');
      expect(command?.template).toContain('$ARGUMENTS');
      expect(command?.source).toBe('project');
    });

    it('空模板返回 null', () => {
      const content = `---
description: Empty
---

`;

      const command = loader.parseMarkdownCommand(content, 'empty', 'user');

      expect(command).toBeNull();
    });
  });

  describe('loadFromFile - 从文件加载', () => {
    it('从 Markdown 文件加载 Command', async () => {
      const mdContent = `---
description: Test command
---

Test template $ARGUMENTS
`;

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

      const command = await loader.loadFromFile(
        '/base/commands/test.md',
        '/base/commands',
        'user'
      );

      expect(command).not.toBeNull();
      expect(command?.name).toBe('test');
      expect(command?.description).toBe('Test command');
    });

    it('处理嵌套目录路径', async () => {
      const mdContent = `---
description: Deploy staging
---

Deploy to staging
`;

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

      const command = await loader.loadFromFile(
        '/base/commands/deploy/staging.md',
        '/base/commands',
        'project'
      );

      expect(command).not.toBeNull();
      expect(command?.name).toBe('deploy/staging');
    });
  });

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

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

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

    it('加载目录中的所有 Markdown 文件', async () => {
      vi.mocked(fs.access).mockResolvedValue(undefined);
      vi.mocked(fs.readdir).mockResolvedValue([
        { name: 'cmd1.md', isFile: () => true, isDirectory: () => false },
        { name: 'cmd2.md', isFile: () => true, isDirectory: () => false },
        { name: 'readme.txt', isFile: () => true, isDirectory: () => false }, // 非 .md
      ] as any);

      vi.mocked(fs.readFile)
        .mockResolvedValueOnce('---\ndescription: Cmd1\n---\nTemplate 1')
        .mockResolvedValueOnce('---\ndescription: Cmd2\n---\nTemplate 2');

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

      expect(commands.length).toBe(2);
      expect(commands.map((c) => c.name)).toContain('cmd1');
      expect(commands.map((c) => c.name)).toContain('cmd2');
    });

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

      vi.mocked(fs.readFile)
        .mockResolvedValueOnce('Root template')
        .mockResolvedValueOnce('Sub template');

      const commands = await loader.loadFromDirectory('/test', 'project');

      expect(commands.length).toBe(2);
      expect(commands.map((c) => c.name)).toContain('root');
      expect(commands.map((c) => c.name)).toContain('subdir/sub');
    });

    it('跳过隐藏目录', async () => {
      vi.mocked(fs.access).mockResolvedValue(undefined);
      vi.mocked(fs.readdir).mockResolvedValue([
        { name: '.hidden', isFile: () => false, isDirectory: () => true },
        { name: 'visible.md', isFile: () => true, isDirectory: () => false },
      ] as any);

      vi.mocked(fs.readFile).mockResolvedValue('Template');

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

      expect(commands.length).toBe(1);
      expect(commands[0].name).toBe('visible');
    });
  });

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

      const dir = loader.getUserCommandsDir();

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

      process.env.HOME = originalHome;
    });

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

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