/**
 * Commands Route 测试
 *
 * 测试斜杠命令管理 REST API 端点
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { Hono } from 'hono';

// Create mock command module
const mockCommandRegistry = vi.hoisted(() => ({
  initialize: vi.fn().mockResolvedValue(undefined),
  reload: vi.fn().mockResolvedValue(undefined),
  get: vi.fn(),
  getAll: vi.fn(),
  list: vi.fn(),
  search: vi.fn(),
  getStats: vi.fn(),
}));

const mockCommandExecutor = vi.hoisted(() => ({
  execute: vi.fn(),
}));

const mockCommandManager = vi.hoisted(() => ({
  create: vi.fn(),
  update: vi.fn(),
  delete: vi.fn(),
  getContent: vi.fn(),
}));

const mockCommandModule = vi.hoisted(() => ({
  getCommandRegistry: vi.fn(() => mockCommandRegistry),
  createCommandExecutor: vi.fn(() => mockCommandExecutor),
  createCommandManager: vi.fn(() => mockCommandManager),
}));

// Track if module should be available
let moduleAvailable = true;

vi.mock('@ai-assistant/core', () => {
  if (!moduleAvailable) {
    throw new Error('Module not found');
  }
  return mockCommandModule;
});

vi.mock('../../../src/routes/config.js', () => ({
  getConfig: vi.fn(() => ({ workdir: '/test/workdir' })),
}));

import { commandsRouter } from '../../../src/routes/commands.js';

// Create test app
const app = new Hono();
app.route('/commands', commandsRouter);

describe('Commands Route', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    moduleAvailable = true;
    mockCommandRegistry.list.mockReturnValue([]);
    mockCommandRegistry.getStats.mockReturnValue({ total: 0, bySource: {} });
  });

  describe('GET /commands - 列出所有命令', () => {
    it('返回命令列表', async () => {
      mockCommandRegistry.list.mockReturnValue([
        { name: 'test', description: 'Test command', source: 'builtin' },
        { name: 'deploy', description: 'Deploy command', source: 'project' },
      ]);
      mockCommandRegistry.getStats.mockReturnValue({
        total: 2,
        bySource: { builtin: 1, project: 1 },
      });

      const res = await app.request('/commands');
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
      expect(json.data.commands).toHaveLength(2);
      expect(json.data.stats.total).toBe(2);
    });

    it('返回空列表', async () => {
      const res = await app.request('/commands');
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.data.commands).toEqual([]);
    });
  });

  describe('POST /commands/search - 搜索命令', () => {
    it('搜索返回结果', async () => {
      mockCommandRegistry.search.mockReturnValue([
        {
          command: { name: 'test', description: 'Test', source: 'builtin', template: 'echo test' },
          score: 0.9,
        },
      ]);

      const res = await app.request('/commands/search', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ query: 'test', limit: 10 }),
      });
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
      expect(json.data).toHaveLength(1);
      expect(json.data[0].score).toBe(0.9);
    });

    it('缺少查询参数返回 400', async () => {
      const res = await app.request('/commands/search', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({}),
      });
      const json = await res.json();

      expect(res.status).toBe(400);
      expect(json.success).toBe(false);
    });
  });

  describe('POST /commands/reload - 重新加载命令', () => {
    it('重新加载成功', async () => {
      mockCommandRegistry.getStats.mockReturnValue({
        total: 5,
        bySource: { builtin: 3, project: 2 },
      });

      const res = await app.request('/commands/reload', { method: 'POST' });
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
      expect(json.data.message).toBe('Commands reloaded');
      expect(mockCommandRegistry.reload).toHaveBeenCalled();
    });
  });

  describe('GET /commands/:name - 获取命令详情', () => {
    it('返回命令详情', async () => {
      mockCommandRegistry.get.mockReturnValue({
        name: 'test',
        description: 'Test command',
        template: 'echo test',
        source: 'builtin',
      });

      const res = await app.request('/commands/test');
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
      expect(json.data.name).toBe('test');
      expect(json.data.hasTemplate).toBe(true);
    });

    it('命令不存在返回 404', async () => {
      mockCommandRegistry.get.mockReturnValue(undefined);

      const res = await app.request('/commands/non-existent');
      const json = await res.json();

      expect(res.status).toBe(404);
      expect(json.success).toBe(false);
      expect(json.error).toContain('not found');
    });

    it('支持包含斜杠的命令名', async () => {
      mockCommandRegistry.get.mockReturnValue({
        name: 'deploy/staging',
        description: 'Deploy to staging',
        template: 'deploy staging',
        source: 'project',
      });

      const res = await app.request('/commands/deploy/staging');
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.data.name).toBe('deploy/staging');
    });
  });

  describe('POST /commands/:name/execute - 执行命令', () => {
    it('执行成功', async () => {
      mockCommandRegistry.get.mockReturnValue({
        name: 'test',
        template: 'echo {{args}}',
        source: 'builtin',
      });
      mockCommandExecutor.execute.mockResolvedValue({
        success: true,
        prompt: 'echo hello',
      });

      const res = await app.request('/commands/test/execute', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ arguments: 'hello' }),
      });
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
      expect(json.data.prompt).toBe('echo hello');
    });

    it('执行失败返回 400', async () => {
      mockCommandExecutor.execute.mockResolvedValue({
        success: false,
        error: 'Command not found',
      });

      const res = await app.request('/commands/test/execute', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({}),
      });
      const json = await res.json();

      expect(res.status).toBe(400);
      expect(json.success).toBe(false);
    });

    it('无请求体时默认空参数', async () => {
      mockCommandExecutor.execute.mockResolvedValue({
        success: true,
        prompt: 'test',
      });

      const res = await app.request('/commands/test/execute', {
        method: 'POST',
      });
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(mockCommandExecutor.execute).toHaveBeenCalled();
    });
  });

  describe('POST /commands - 创建命令', () => {
    it('创建成功', async () => {
      mockCommandManager.create.mockResolvedValue({
        success: true,
        path: '/test/.claude/commands/my-command.md',
      });

      const res = await app.request('/commands', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name: 'my-command',
          description: 'My command',
          template: 'echo hello',
          scope: 'project',
        }),
      });
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
      expect(json.data.name).toBe('my-command');
    });

    it('缺少必需字段返回 400', async () => {
      const res = await app.request('/commands', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name: 'test' }),
      });
      const json = await res.json();

      expect(res.status).toBe(400);
      expect(json.success).toBe(false);
    });

    it('创建失败返回错误', async () => {
      mockCommandManager.create.mockResolvedValue({
        success: false,
        error: 'Command already exists',
      });

      const res = await app.request('/commands', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name: 'existing',
          template: 'echo test',
          scope: 'project',
        }),
      });
      const json = await res.json();

      expect(res.status).toBe(400);
      expect(json.error).toContain('already exists');
    });
  });

  describe('GET /commands/:name/content - 获取命令完整内容', () => {
    it('获取成功，返回包含 template 的完整内容', async () => {
      mockCommandManager.getContent.mockResolvedValue({
        success: true,
        data: {
          name: 'test',
          description: 'Test command',
          template: 'echo test',
          source: 'project',
          sourcePath: '/test/.claude/commands/test.md',
        },
      });

      const res = await app.request('/commands/test/content');
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
      expect(json.data.template).toBe('echo test');
      expect(json.data.name).toBe('test');
    });

    it('支持包含斜杠的命令名', async () => {
      mockCommandManager.getContent.mockResolvedValue({
        success: true,
        data: {
          name: 'deploy/staging',
          template: 'deploy to staging',
          source: 'project',
        },
      });

      const res = await app.request('/commands/deploy/staging/content');
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
      expect(json.data.template).toBe('deploy to staging');
    });

    it('命令不存在返回 404', async () => {
      mockCommandManager.getContent.mockResolvedValue({
        success: false,
        error: 'Command not found: non-existent',
      });

      const res = await app.request('/commands/non-existent/content');
      const json = await res.json();

      expect(res.status).toBe(404);
      expect(json.success).toBe(false);
      expect(json.error).toContain('not found');
    });
  });

  describe('PUT /commands/:name - 更新命令', () => {
    it('更新成功', async () => {
      mockCommandManager.update.mockResolvedValue({
        success: true,
        path: '/test/.claude/commands/test.md',
      });

      const res = await app.request('/commands/test', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ template: 'echo updated' }),
      });
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
    });

    it('更新失败返回 400', async () => {
      mockCommandManager.update.mockResolvedValue({
        success: false,
        error: 'Cannot update builtin command',
      });

      const res = await app.request('/commands/builtin-cmd', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ template: 'new template' }),
      });
      const json = await res.json();

      expect(res.status).toBe(400);
      expect(json.success).toBe(false);
    });
  });

  describe('DELETE /commands/:name - 删除命令', () => {
    it('删除成功', async () => {
      mockCommandManager.delete.mockResolvedValue({
        success: true,
        path: '/test/.claude/commands/test.md',
      });

      const res = await app.request('/commands/test', {
        method: 'DELETE',
      });
      const json = await res.json();

      expect(res.status).toBe(200);
      expect(json.success).toBe(true);
    });

    it('删除失败返回 400', async () => {
      mockCommandManager.delete.mockResolvedValue({
        success: false,
        error: 'Cannot delete builtin command',
      });

      const res = await app.request('/commands/builtin-cmd', {
        method: 'DELETE',
      });
      const json = await res.json();

      expect(res.status).toBe(400);
      expect(json.success).toBe(false);
    });
  });
});
