import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
  loadAgentConfig,
  saveAgentConfig,
  getConfigTemplate,
} from '../../../src/agent/config-loader.js';
import type { AgentConfigFile } from '../../../src/agent/types.js';

// Mock fs
vi.mock('fs', () => ({
  existsSync: vi.fn(),
  promises: {
    readFile: vi.fn(),
    writeFile: vi.fn(),
    mkdir: vi.fn(),
  },
}));

// Mock js-yaml
vi.mock('js-yaml', () => ({
  load: vi.fn((content: string) => JSON.parse(content)),
  dump: vi.fn((obj: unknown) => JSON.stringify(obj, null, 2)),
}));

import * as fs from 'fs';

describe('loadAgentConfig - 加载 Agent 配置', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('无配置文件时返回 null', async () => {
    vi.mocked(fs.existsSync).mockReturnValue(false);

    const config = await loadAgentConfig('/test/project');

    expect(config).toBeNull();
  });

  it('加载 JSON 配置文件', async () => {
    const mockConfig: AgentConfigFile = {
      defaults: {
        maxSteps: 20,
      },
      agents: {
        'custom-agent': {
          description: '自定义 Agent',
          mode: 'subagent',
          prompt: '你是助手',
        },
      },
    };

    vi.mocked(fs.existsSync).mockImplementation((path: unknown) =>
      String(path).endsWith('.json')
    );
    vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify(mockConfig));

    const config = await loadAgentConfig('/test/project');

    expect(config).not.toBeNull();
    expect(config?.defaults?.maxSteps).toBe(20);
    expect(config?.agents?.['custom-agent']).toBeDefined();
  });

  it('加载 YAML 配置文件', async () => {
    const mockConfig: AgentConfigFile = {
      defaults: {
        maxSteps: 15,
      },
      agents: {},
    };

    vi.mocked(fs.existsSync).mockImplementation((path: unknown) =>
      String(path).endsWith('.yaml')
    );
    vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify(mockConfig));

    const config = await loadAgentConfig('/test/project');

    expect(config).not.toBeNull();
  });

  it('无效配置格式返回 null', async () => {
    vi.mocked(fs.existsSync).mockReturnValue(true);
    vi.mocked(fs.promises.readFile).mockResolvedValue('invalid json');

    // 会在解析时失败
    const config = await loadAgentConfig('/test/project');

    // 取决于实现，可能是 null 或抛出错误后 null
    expect(config).toBeNull();
  });

  it('配置搜索顺序', async () => {
    // 测试搜索多个路径
    const calls: string[] = [];
    vi.mocked(fs.existsSync).mockImplementation((path: unknown) => {
      calls.push(String(path));
      return false;
    });

    await loadAgentConfig('/test/project');

    // 应该搜索多个配置路径
    expect(calls.length).toBeGreaterThan(0);
    expect(calls.some(p => p.includes('.ai-assist'))).toBe(true);
  });
});

describe('saveAgentConfig - 保存 Agent 配置', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(fs.existsSync).mockReturnValue(false);
  });

  it('保存 JSON 格式配置', async () => {
    const config: AgentConfigFile = {
      defaults: {
        maxSteps: 10,
      },
      agents: {},
    };

    await saveAgentConfig('/test/project', config, 'json');

    expect(fs.promises.mkdir).toHaveBeenCalled();
    expect(fs.promises.writeFile).toHaveBeenCalledWith(
      expect.stringContaining('agents.json'),
      expect.any(String),
      'utf-8'
    );
  });

  it('保存 YAML 格式配置', async () => {
    const config: AgentConfigFile = {
      defaults: {
        maxSteps: 10,
      },
      agents: {},
    };

    await saveAgentConfig('/test/project', config, 'yaml');

    expect(fs.promises.writeFile).toHaveBeenCalledWith(
      expect.stringContaining('agents.yaml'),
      expect.any(String),
      'utf-8'
    );
  });

  it('创建配置目录如果不存在', async () => {
    vi.mocked(fs.existsSync).mockReturnValue(false);

    await saveAgentConfig('/test/project', { agents: {} }, 'json');

    expect(fs.promises.mkdir).toHaveBeenCalledWith(
      expect.stringContaining('.ai-assist'),
      { recursive: true }
    );
  });

  it('目录已存在时不重复创建', async () => {
    vi.mocked(fs.existsSync).mockReturnValue(true);

    await saveAgentConfig('/test/project', { agents: {} }, 'json');

    expect(fs.promises.mkdir).not.toHaveBeenCalled();
  });
});

describe('getConfigTemplate - 获取配置模板', () => {
  it('返回有效的配置模板', () => {
    const template = getConfigTemplate();

    expect(template).toBeDefined();
    expect(template.defaults).toBeDefined();
    expect(template.agents).toBeDefined();
  });

  it('模板包含默认配置', () => {
    const template = getConfigTemplate();

    expect(template.defaults?.maxSteps).toBeDefined();
    expect(template.defaults?.model).toBeDefined();
  });

  it('模板包含示例 Agent', () => {
    const template = getConfigTemplate();

    expect(template.agents).toBeDefined();
    expect(Object.keys(template.agents || {}).length).toBeGreaterThan(0);
  });

  it('示例 Agent 包含必要字段', () => {
    const template = getConfigTemplate();
    const agents = template.agents || {};
    const firstAgent = Object.values(agents)[0];

    expect(firstAgent).toBeDefined();
    expect(firstAgent.description).toBeDefined();
    expect(firstAgent.mode).toBeDefined();
  });

  it('模板包含权限配置示例', () => {
    const template = getConfigTemplate();

    expect(template.defaults?.permission).toBeDefined();
    expect(template.defaults?.permission?.bash).toBeDefined();
  });
});

describe('配置验证', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('空对象是有效配置', async () => {
    vi.mocked(fs.existsSync).mockReturnValue(true);
    vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify({}));

    const config = await loadAgentConfig('/test/project');

    expect(config).not.toBeNull();
  });

  it('只有 defaults 的配置有效', async () => {
    const mockConfig = {
      defaults: {
        maxSteps: 10,
      },
    };

    vi.mocked(fs.existsSync).mockReturnValue(true);
    vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify(mockConfig));

    const config = await loadAgentConfig('/test/project');

    expect(config).not.toBeNull();
    expect(config?.defaults?.maxSteps).toBe(10);
  });

  it('只有 agents 的配置有效', async () => {
    const mockConfig = {
      agents: {
        'test-agent': {
          description: 'Test',
          mode: 'subagent',
        },
      },
    };

    vi.mocked(fs.existsSync).mockReturnValue(true);
    vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify(mockConfig));

    const config = await loadAgentConfig('/test/project');

    expect(config).not.toBeNull();
    expect(config?.agents?.['test-agent']).toBeDefined();
  });

  it('defaults 为非对象时配置无效', async () => {
    const mockConfig = {
      defaults: 'invalid',
    };

    vi.mocked(fs.existsSync).mockReturnValue(true);
    vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify(mockConfig));

    const config = await loadAgentConfig('/test/project');

    // 应该返回 null（无效配置）
    expect(config).toBeNull();
  });

  it('agents 为非对象时配置无效', async () => {
    const mockConfig = {
      agents: 'invalid',
    };

    vi.mocked(fs.existsSync).mockReturnValue(true);
    vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify(mockConfig));

    const config = await loadAgentConfig('/test/project');

    expect(config).toBeNull();
  });
});
