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

// Mock functions
const mockTouchFile = vi.fn();
const mockGetDiagnostics = vi.fn();
const mockGetFileDiagnostics = vi.fn();
const mockShutdown = vi.fn();

// 创建 mock class
class MockLSPClientManager {
  touchFile = mockTouchFile;
  getDiagnostics = mockGetDiagnostics;
  getFileDiagnostics = mockGetFileDiagnostics;
  shutdown = mockShutdown;

  constructor(_rootPath?: string) {}
}

// Mock client module
vi.mock('../../../src/lsp/client.js', () => ({
  LSPClientManager: MockLSPClientManager,
}));

// Mock language module
vi.mock('../../../src/lsp/language.js', () => ({
  getLanguageId: vi.fn((path: string) => {
    if (path.endsWith('.ts')) return 'typescript';
    if (path.endsWith('.py')) return 'python';
    return undefined;
  }),
  isLanguageSupported: vi.fn((path: string) => {
    return path.endsWith('.ts') || path.endsWith('.py');
  }),
  getSupportedExtensions: vi.fn(() => ['.ts', '.js', '.py']),
}));

// Mock server module
vi.mock('../../../src/lsp/server.js', () => ({
  getServerConfig: vi.fn(),
  hasServerConfig: vi.fn(),
  getSupportedLanguages: vi.fn(() => []),
}));

describe('LSP Index - LSP 模块入口', () => {
  let lspModule: typeof import('../../../src/lsp/index.js');

  beforeEach(async () => {
    vi.clearAllMocks();
    // 重置模块缓存以获取干净的状态
    vi.resetModules();
    // 重新导入模块
    lspModule = await import('../../../src/lsp/index.js');
  });

  describe('initLSP - 初始化 LSP', () => {
    it('初始化 LSP 管理器', () => {
      lspModule.initLSP('/test/project');

      expect(lspModule.getLSPManager()).not.toBeNull();
    });

    it('重复初始化不会创建新实例', () => {
      lspModule.initLSP('/test/project');
      const manager1 = lspModule.getLSPManager();

      lspModule.initLSP('/another/project');
      const manager2 = lspModule.getLSPManager();

      expect(manager1).toBe(manager2);
    });

    it('默认使用 process.cwd()', () => {
      lspModule.initLSP();

      expect(lspModule.getLSPManager()).not.toBeNull();
    });
  });

  describe('getLSPManager - 获取管理器', () => {
    it('未初始化时返回 null', async () => {
      // 重置模块获取干净状态
      vi.resetModules();
      const freshModule = await import('../../../src/lsp/index.js');

      expect(freshModule.getLSPManager()).toBeNull();
    });

    it('初始化后返回管理器实例', () => {
      lspModule.initLSP();

      expect(lspModule.getLSPManager()).not.toBeNull();
    });
  });

  describe('touchFile - 通知文件变更', () => {
    it('未初始化时返回 false', async () => {
      vi.resetModules();
      const freshModule = await import('../../../src/lsp/index.js');

      const result = await freshModule.touchFile('/test/file.ts');

      expect(result).toBe(false);
    });

    it('不支持的语言返回 false', async () => {
      lspModule.initLSP();

      const result = await lspModule.touchFile('/test/file.txt');

      expect(result).toBe(false);
    });

    it('支持的语言调用管理器', async () => {
      mockTouchFile.mockResolvedValue(true);
      lspModule.initLSP();

      const result = await lspModule.touchFile('/test/file.ts');

      expect(mockTouchFile).toHaveBeenCalledWith('/test/file.ts', false);
      expect(result).toBe(true);
    });

    it('新文件传递 isNew 参数', async () => {
      mockTouchFile.mockResolvedValue(true);
      lspModule.initLSP();

      await lspModule.touchFile('/test/file.ts', true);

      expect(mockTouchFile).toHaveBeenCalledWith('/test/file.ts', true);
    });

    it('touchFile 出错时静默返回 false', async () => {
      mockTouchFile.mockRejectedValue(new Error('LSP error'));
      lspModule.initLSP();

      const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
      const result = await lspModule.touchFile('/test/file.ts');

      expect(result).toBe(false);
      consoleErrorSpy.mockRestore();
    });
  });

  describe('getDiagnostics - 获取所有诊断', () => {
    it('未初始化时返回空 Map', async () => {
      vi.resetModules();
      const freshModule = await import('../../../src/lsp/index.js');

      const result = freshModule.getDiagnostics();

      expect(result).toBeInstanceOf(Map);
      expect(result.size).toBe(0);
    });

    it('返回管理器的诊断信息', () => {
      const mockDiagnostics = new Map([
        ['/test/file.ts', [{ file: '/test/file.ts', line: 1, column: 1, severity: 'error', message: 'Test error' }]],
      ]);
      mockGetDiagnostics.mockReturnValue(mockDiagnostics);
      lspModule.initLSP();

      const result = lspModule.getDiagnostics();

      expect(result).toBe(mockDiagnostics);
    });
  });

  describe('getFileDiagnostics - 获取单文件诊断', () => {
    it('未初始化时返回空数组', async () => {
      vi.resetModules();
      const freshModule = await import('../../../src/lsp/index.js');

      const result = freshModule.getFileDiagnostics('/test/file.ts');

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

    it('返回指定文件的诊断', () => {
      const mockDiagnostics = [
        { file: '/test/file.ts', line: 1, column: 1, severity: 'error' as const, message: 'Error' },
      ];
      mockGetFileDiagnostics.mockReturnValue(mockDiagnostics);
      lspModule.initLSP();

      const result = lspModule.getFileDiagnostics('/test/file.ts');

      expect(result).toBe(mockDiagnostics);
    });
  });

  describe('formatDiagnostics - 格式化诊断信息', () => {
    it('空诊断返回空字符串', () => {
      const result = lspModule.formatDiagnostics([]);

      expect(result).toBe('');
    });

    it('格式化单个诊断', () => {
      const diagnostics = [
        {
          file: '/test/file.ts',
          line: 10,
          column: 5,
          severity: 'error' as const,
          message: 'Type error',
          code: 'TS2345',
          source: 'typescript',
        },
      ];

      const result = lspModule.formatDiagnostics(diagnostics);

      expect(result).toContain('10:5');
      expect(result).toContain('ERROR');
      expect(result).toContain('TS2345');
      expect(result).toContain('Type error');
      expect(result).toContain('typescript');
    });

    it('格式化多个诊断', () => {
      const diagnostics = [
        { file: '/test/file.ts', line: 1, column: 1, severity: 'error' as const, message: 'Error 1' },
        { file: '/test/file.ts', line: 2, column: 2, severity: 'warning' as const, message: 'Warning 1' },
      ];

      const result = lspModule.formatDiagnostics(diagnostics);

      expect(result).toContain('Error 1');
      expect(result).toContain('Warning 1');
      expect(result).toContain('ERROR');
      expect(result).toContain('WARNING');
    });

    it('无 code 时不显示 code', () => {
      const diagnostics = [
        { file: '/test/file.ts', line: 1, column: 1, severity: 'error' as const, message: 'Error' },
      ];

      const result = lspModule.formatDiagnostics(diagnostics);

      expect(result).not.toContain('[');
    });

    it('无 source 时不显示 source', () => {
      const diagnostics = [
        { file: '/test/file.ts', line: 1, column: 1, severity: 'error' as const, message: 'Error' },
      ];

      const result = lspModule.formatDiagnostics(diagnostics);

      expect(result).not.toContain('(');
    });
  });

  describe('getFormattedFileDiagnostics - 获取格式化的文件诊断', () => {
    it('无错误和警告时返回空字符串', async () => {
      mockGetFileDiagnostics.mockReturnValue([
        { file: '/test/file.ts', line: 1, column: 1, severity: 'info', message: 'Info' },
        { file: '/test/file.ts', line: 2, column: 2, severity: 'hint', message: 'Hint' },
      ]);
      lspModule.initLSP();

      const result = await lspModule.getFormattedFileDiagnostics('/test/file.ts');

      expect(result).toBe('');
    });

    it('只显示错误和警告', async () => {
      mockGetFileDiagnostics.mockReturnValue([
        { file: '/test/file.ts', line: 1, column: 1, severity: 'error', message: 'Error' },
        { file: '/test/file.ts', line: 2, column: 2, severity: 'warning', message: 'Warning' },
        { file: '/test/file.ts', line: 3, column: 3, severity: 'info', message: 'Info' },
      ]);
      lspModule.initLSP();

      const result = await lspModule.getFormattedFileDiagnostics('/test/file.ts');

      expect(result).toContain('Error');
      expect(result).toContain('Warning');
      expect(result).not.toContain('Info');
    });

    it('包含 file_diagnostics 标签', async () => {
      mockGetFileDiagnostics.mockReturnValue([
        { file: '/test/file.ts', line: 1, column: 1, severity: 'error', message: 'Error' },
      ]);
      lspModule.initLSP();

      const result = await lspModule.getFormattedFileDiagnostics('/test/file.ts');

      expect(result).toContain('<file_diagnostics');
      expect(result).toContain('/test/file.ts');
      expect(result).toContain('</file_diagnostics>');
    });

    it('显示错误和警告数量', async () => {
      mockGetFileDiagnostics.mockReturnValue([
        { file: '/test/file.ts', line: 1, column: 1, severity: 'error', message: 'Error 1' },
        { file: '/test/file.ts', line: 2, column: 2, severity: 'error', message: 'Error 2' },
        { file: '/test/file.ts', line: 3, column: 3, severity: 'warning', message: 'Warning' },
      ]);
      lspModule.initLSP();

      const result = await lspModule.getFormattedFileDiagnostics('/test/file.ts');

      expect(result).toContain('2 个错误');
      expect(result).toContain('1 个警告');
    });
  });

  describe('shutdownLSP - 关闭 LSP', () => {
    it('关闭管理器', async () => {
      lspModule.initLSP();

      await lspModule.shutdownLSP();

      expect(mockShutdown).toHaveBeenCalled();
      expect(lspModule.getLSPManager()).toBeNull();
    });

    it('未初始化时安全调用', async () => {
      vi.resetModules();
      const freshModule = await import('../../../src/lsp/index.js');

      await expect(freshModule.shutdownLSP()).resolves.not.toThrow();
    });
  });

  describe('导出', () => {
    it('导出 getLanguageId', () => {
      expect(lspModule.getLanguageId).toBeDefined();
    });

    it('导出 isLanguageSupported', () => {
      expect(lspModule.isLanguageSupported).toBeDefined();
    });

    it('导出 getSupportedExtensions', () => {
      expect(lspModule.getSupportedExtensions).toBeDefined();
    });

    it('导出 LSPClientManager', () => {
      expect(lspModule.LSPClientManager).toBeDefined();
    });
  });
});
