import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
  CommandRegistry,
  getCommandRegistry,
  resetCommandRegistry,
} from '../../../src/commands/registry.js';
import type { Command } from '../../../src/commands/types.js';

// Mock loader
vi.mock('../../../src/commands/loader.js', () => ({
  commandLoader: {
    loadFromDirectory: vi.fn().mockResolvedValue([]),
    getUserCommandsDir: vi.fn().mockReturnValue('/mock/user/commands'),
    getProjectCommandsDir: vi.fn().mockReturnValue('/mock/project/commands'),
  },
}));

// Mock builtin commands
vi.mock('../../../src/commands/builtin/index.js', () => ({
  builtinCommands: [
    {
      name: 'test-builtin',
      description: '内置测试命令',
      template: '测试模板 $ARGUMENTS',
      source: 'builtin',
    },
  ],
}));

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

  beforeEach(() => {
    resetCommandRegistry();
    registry = new CommandRegistry();
  });

  describe('register - 注册', () => {
    it('成功注册 Command', () => {
      const command: Command = {
        name: 'test',
        description: '测试',
        template: '模板',
        source: 'user',
      };

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

    it('高优先级可以覆盖低优先级', () => {
      const builtin: Command = {
        name: 'same',
        description: '内置版本',
        template: '内置模板',
        source: 'builtin',
      };

      const project: Command = {
        name: 'same',
        description: '项目版本',
        template: '项目模板',
        source: 'project',
      };

      registry.register(builtin);
      registry.register(project);

      const result = registry.get('same');
      expect(result?.source).toBe('project');
      expect(result?.description).toBe('项目版本');
    });

    it('低优先级不能覆盖高优先级', () => {
      const project: Command = {
        name: 'same',
        description: '项目版本',
        template: '项目模板',
        source: 'project',
      };

      const builtin: Command = {
        name: 'same',
        description: '内置版本',
        template: '内置模板',
        source: 'builtin',
      };

      registry.register(project);
      registry.register(builtin);

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

  describe('get - 获取', () => {
    it('获取存在的 Command', () => {
      const command: Command = {
        name: 'test',
        description: '测试',
        template: '模板',
        source: 'user',
      };

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

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

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

  describe('search - 搜索', () => {
    beforeEach(() => {
      const commands: Command[] = [
        {
          name: 'review',
          description: '代码审查',
          template: '审查模板',
          source: 'builtin',
        },
        {
          name: 'test',
          description: '运行测试',
          template: '测试模板',
          source: 'builtin',
        },
        {
          name: 'deploy/staging',
          description: '部署到 staging',
          template: '部署模板',
          source: 'project',
        },
      ];

      for (const cmd of commands) {
        registry.register(cmd);
      }
    });

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

    it('按名称前缀匹配', () => {
      const results = registry.search('deploy');
      expect(results.length).toBeGreaterThan(0);
      expect(results[0].command.name).toBe('deploy/staging');
    });

    it('按描述匹配', () => {
      const results = registry.search('代码');
      expect(results.length).toBeGreaterThan(0);
      expect(results[0].command.name).toBe('review');
    });

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

  describe('list - 列表', () => {
    it('返回所有 Commands 的摘要', () => {
      const command: Command = {
        name: 'test',
        description: '测试',
        template: '模板',
        source: 'user',
      };

      registry.register(command);
      const list = registry.list();

      expect(list.length).toBe(1);
      expect(list[0].name).toBe('test');
      expect(list[0].description).toBe('测试');
      expect(list[0].source).toBe('user');
    });

    it('按名称排序', () => {
      const commands: Command[] = [
        { name: 'zebra', description: '', template: '', source: 'user' },
        { name: 'alpha', description: '', template: '', source: 'user' },
        { name: 'beta', description: '', template: '', source: 'user' },
      ];

      for (const cmd of commands) {
        registry.register(cmd);
      }

      const list = registry.list();
      expect(list[0].name).toBe('alpha');
      expect(list[1].name).toBe('beta');
      expect(list[2].name).toBe('zebra');
    });
  });

  describe('getStats - 统计', () => {
    it('返回正确的统计信息', () => {
      const commands: Command[] = [
        { name: 'b1', description: '', template: '', source: 'builtin' },
        { name: 'b2', description: '', template: '', source: 'builtin' },
        { name: 'u1', description: '', template: '', source: 'user' },
        { name: 'p1', description: '', template: '', source: 'project' },
      ];

      for (const cmd of commands) {
        registry.register(cmd);
      }

      const stats = registry.getStats();

      expect(stats.total).toBe(4);
      expect(stats.bySource.builtin).toBe(2);
      expect(stats.bySource.user).toBe(1);
      expect(stats.bySource.project).toBe(1);
    });
  });
});

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

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

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

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

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