import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';

// Mock fs module
vi.mock('fs', () => ({
  existsSync: vi.fn(),
  readFileSync: vi.fn(),
  writeFileSync: vi.fn(),
  mkdirSync: vi.fn(),
}));

// Mock provider registry
vi.mock('../../../src/provider/index.js', () => ({
  providerRegistry: {
    getInfo: vi.fn(),
    getConfig: vi.fn(),
  },
  resolveApiKey: vi.fn(),
}));

// Mock process.exit
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
  throw new Error('process.exit called');
});

// Mock console.error
const mockConsoleError = vi.spyOn(console, 'error').mockImplementation(() => {});

describe('Config - 配置管理扩展测试', () => {
  const CONFIG_DIR = path.join(os.homedir(), '.ai-terminal-assistant');
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');

  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    mockExit.mockClear();
    mockConsoleError.mockClear();
  });

  describe('getConfig - 获取原始配置', () => {
    it('配置文件不存在返回空对象', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(false);

      vi.resetModules();
      const { getConfig } = await import('../../../src/utils/config.js');

      const config = getConfig();
      expect(config).toEqual({});
    });

    it('配置文件存在返回解析后的内容', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(true);
      vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
        provider: 'anthropic',
        model: 'claude-sonnet-4-20250514',
      }));

      vi.resetModules();
      const { getConfig } = await import('../../../src/utils/config.js');

      const config = getConfig();
      expect(config.provider).toBe('anthropic');
      expect(config.model).toBe('claude-sonnet-4-20250514');
    });

    it('配置文件解析错误返回空对象', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(true);
      vi.mocked(fs.readFileSync).mockReturnValue('invalid json');

      vi.resetModules();
      const { getConfig } = await import('../../../src/utils/config.js');

      const config = getConfig();
      expect(config).toEqual({});
    });
  });

  describe('loadConfig - 加载配置', () => {
    it('通过 ProviderRegistry 获取 API Key', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(true);
      vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
        provider: 'anthropic',
      }));

      vi.resetModules();
      const { resolveApiKey } = await import('../../../src/provider/index.js');
      const { loadConfig } = await import('../../../src/utils/config.js');

      vi.mocked(resolveApiKey).mockReturnValue('resolved-api-key');

      const config = loadConfig();
      expect(config.apiKey).toBe('resolved-api-key');
    });

    it('使用配置文件中的 provider', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(true);
      vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
        provider: 'deepseek',
      }));

      vi.resetModules();
      const { resolveApiKey } = await import('../../../src/provider/index.js');
      const { loadConfig } = await import('../../../src/utils/config.js');

      vi.mocked(resolveApiKey).mockReturnValue('deepseek-key');

      const config = loadConfig();
      expect(config.provider).toBe('deepseek');
      expect(config.apiKey).toBe('deepseek-key');
    });

    it('使用默认模型', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(false);

      vi.resetModules();
      const { resolveApiKey } = await import('../../../src/provider/index.js');
      const { loadConfig } = await import('../../../src/utils/config.js');

      vi.mocked(resolveApiKey).mockReturnValue('test-key');

      const config = loadConfig();
      expect(config.model).toBe('claude-sonnet-4-20250514');
    });

    it('无 API Key 时退出程序', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(false);

      vi.resetModules();
      const { resolveApiKey } = await import('../../../src/provider/index.js');
      const { loadConfig } = await import('../../../src/utils/config.js');

      vi.mocked(resolveApiKey).mockReturnValue(undefined);

      expect(() => loadConfig()).toThrow('process.exit called');
      expect(mockConsoleError).toHaveBeenCalled();
    });
  });

  describe('loadVisionConfig - 加载 Vision 配置', () => {
    it('返回 null 当没有 API Key', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(false);

      vi.resetModules();
      const { resolveApiKey } = await import('../../../src/provider/index.js');
      const { loadVisionConfig } = await import('../../../src/utils/config.js');

      vi.mocked(resolveApiKey).mockReturnValue(undefined);

      const config = loadVisionConfig();
      expect(config).toBeNull();
    });

    it('从配置文件获取 Vision 设置', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(true);
      vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
        visionProvider: 'openai',
        visionModel: 'gpt-4o',
        visionBaseUrl: 'https://config-vision.example.com',
      }));

      vi.resetModules();
      const { resolveApiKey } = await import('../../../src/provider/index.js');
      const { loadVisionConfig } = await import('../../../src/utils/config.js');

      vi.mocked(resolveApiKey).mockReturnValue('config-vision-key');

      const config = loadVisionConfig();
      expect(config).not.toBeNull();
      expect(config!.provider).toBe('openai');
      expect(config!.model).toBe('gpt-4o');
      expect(config!.apiKey).toBe('config-vision-key');
    });

    it('默认使用 anthropic provider', async () => {
      vi.mocked(fs.existsSync).mockReturnValue(false);

      vi.resetModules();
      const { resolveApiKey } = await import('../../../src/provider/index.js');
      const { loadVisionConfig } = await import('../../../src/utils/config.js');

      vi.mocked(resolveApiKey).mockReturnValue('anthropic-key');

      const config = loadVisionConfig();
      expect(config).not.toBeNull();
      expect(config!.provider).toBe('anthropic');
    });
  });

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

      vi.resetModules();
      const { saveConfig } = await import('../../../src/utils/config.js');

      saveConfig({ provider: 'anthropic' });

      expect(fs.mkdirSync).toHaveBeenCalledWith(CONFIG_DIR, { recursive: true });
    });

    it('合并现有配置', async () => {
      vi.mocked(fs.existsSync).mockImplementation((p) => p === CONFIG_FILE);
      vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
        provider: 'anthropic',
      }));

      vi.resetModules();
      const { saveConfig } = await import('../../../src/utils/config.js');

      saveConfig({ model: 'claude-opus-4-20250514' });

      expect(fs.writeFileSync).toHaveBeenCalledWith(
        CONFIG_FILE,
        expect.stringContaining('"model": "claude-opus-4-20250514"')
      );
      expect(fs.writeFileSync).toHaveBeenCalledWith(
        CONFIG_FILE,
        expect.stringContaining('"provider": "anthropic"')
      );
    });

    it('覆盖相同字段', async () => {
      vi.mocked(fs.existsSync).mockImplementation((p) => p === CONFIG_FILE);
      vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
        provider: 'anthropic',
        model: 'old-model',
      }));

      vi.resetModules();
      const { saveConfig } = await import('../../../src/utils/config.js');

      saveConfig({ model: 'new-model' });

      const writeCall = vi.mocked(fs.writeFileSync).mock.calls[0];
      const savedConfig = JSON.parse(writeCall[1] as string);
      expect(savedConfig.model).toBe('new-model');
    });
  });
});
