import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { setupDocumentationTools } from '../index.js';
import { ConfigManager } from '../../../config/config-manager.js';
import { WorkspaceDataStore } from '../../workspace/store.js';
import { InMemoryFileSystemAdapter } from '../file-system-adapter.js';

// Mock dependencies
vi.mock('../../../config/config-manager.js');
vi.mock('../../workspace/store.js');
const MockConfigManager = ConfigManager as vi.MockedClass<typeof ConfigManager>;
const MockWorkspaceDataStore = WorkspaceDataStore as vi.MockedClass<typeof WorkspaceDataStore>;

describe('Documentation Module', () => {
  let server: Server;
  let configManager: vi.Mocked<ConfigManager>;
  let workspaceStore: vi.Mocked<WorkspaceDataStore>;
  let toolHandler: any;
  let mockFs: InMemoryFileSystemAdapter;

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

    // Setup file system mock
    mockFs = new InMemoryFileSystemAdapter();

    // Setup server mock
    server = {
      setRequestHandler: vi.fn((schema, handler) => {
        toolHandler = handler;
      }),
    } as any;

    // Setup config manager mock
    configManager = new MockConfigManager() as vi.Mocked<ConfigManager>;
    configManager.getStorageManager = vi.fn().mockReturnValue({
      getProjectFilePath: vi.fn((...paths) => {
        if (paths.length === 0) return '/project/root';
        return `/project/root/${paths.join('/')}`;
      }),
      ensureStorageDirectories: vi.fn().mockResolvedValue(undefined),
      loadData: vi.fn().mockResolvedValue(null),
      saveData: vi.fn().mockResolvedValue(undefined),
      getModuleDataPath: vi.fn().mockResolvedValue('/test/data/path')
    } as any);
    configManager.getProjectMode = vi.fn().mockReturnValue('new');
    configManager.isMultiRepo = vi.fn().mockReturnValue(false);
    configManager.getRepositories = vi.fn().mockReturnValue([]);

    // Setup workspace store mock
    workspaceStore = new MockWorkspaceDataStore(configManager) as vi.Mocked<WorkspaceDataStore>;
    workspaceStore.init = vi.fn().mockResolvedValue(undefined);
    workspaceStore.getActiveWorkspace = vi.fn().mockReturnValue(null);

    // Create necessary directories
    await mockFs.mkdir('/project/root', { recursive: true });
    await mockFs.mkdir('/project/root/docs', { recursive: true });

    await setupDocumentationTools(server, configManager, mockFs, workspaceStore);
  });

  describe('generate_readme', () => {
    it('should generate README for single repository project', async () => {
      const args = {
        projectName: 'Test Project',
        description: 'A test project',
        features: ['Feature 1', 'Feature 2'],
        techStack: ['TypeScript', 'Node.js'],
      };

      const result = await toolHandler({
        params: {
          name: 'generate_readme',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('✅ README.md created in your project root!');
      expect(result.structured.file).toBe('README.md');
      expect(result.structured.created).toBe(true);
      
      // Check file was written
      expect(mockFs.hasFile('/project/root/README.md')).toBe(true);
      const content = mockFs.getFileContent('/project/root/README.md');
      expect(content).toContain('# Test Project');
    });

    it('should generate README for specific repository in workspace', async () => {
      const mockWorkspace = {
        id: 'workspace-1',
        name: 'Test Workspace',
        repositories: [
          {
            id: 'repo-1',
            name: 'frontend-repo',
            path: '/workspace/frontend',
            type: 'git' as const,
            primary: true,
          },
          {
            id: 'repo-2',
            name: 'backend-repo',
            path: '/workspace/backend',
            type: 'git' as const,
          },
        ],
      };

      workspaceStore.getActiveWorkspace.mockReturnValue(mockWorkspace as any);
      
      // Create workspace directories BEFORE running test
      await mockFs.mkdir('/workspace', { recursive: true });
      await mockFs.mkdir('/workspace/frontend', { recursive: true });

      const args = {
        projectName: 'Frontend App',
        description: 'React frontend application',
        repository: 'frontend-repo',
      };

      const result = await toolHandler({
        params: {
          name: 'generate_readme',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('✅ README.md created for repository "frontend-repo"!');
      expect(result.structured.repository).toBe('frontend-repo');
      expect(result.structured.workspace).toBe('Test Workspace');
      
      // Check file was written
      expect(mockFs.hasFile('/workspace/frontend/README.md')).toBe(true);
      const content = mockFs.getFileContent('/workspace/frontend/README.md');
      expect(content).toContain('# Frontend App');
    });

    it('should handle missing repository in workspace', async () => {
      const mockWorkspace = {
        repositories: [],
      };

      workspaceStore.getActiveWorkspace.mockReturnValue(mockWorkspace as any);

      const args = {
        projectName: 'Test Project',
        description: 'A test project',
        repository: 'nonexistent-repo',
      };

      await expect(toolHandler({
        params: {
          name: 'generate_readme',
          arguments: args,
        },
      })).rejects.toThrow('Repository "nonexistent-repo" not found');
    });
  });

  describe('generate_claude_config', () => {
    it('should generate CLAUDE.md configuration', async () => {
      const args = {
        tddMode: 'strict' as const,
        testCommand: 'npm test',
        lintCommand: 'npm run lint',
        customInstructions: ['Use TypeScript', 'Follow TDD'],
      };

      const result = await toolHandler({
        params: {
          name: 'generate_claude_config',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('✅ CLAUDE.md created in your project root!');
      expect(result.structured.config.tddMode).toBe('strict');
      // Check file was written
      expect(mockFs.hasFile('/project/root/CLAUDE.md')).toBe(true);
      const content = mockFs.getFileContent('/project/root/CLAUDE.md');
      expect(content).toContain('# Claude Code Configuration');
    });

    it('should generate repository-specific CLAUDE.md', async () => {
      const mockWorkspace = {
        name: 'Test Workspace',
        repositories: [
          {
            name: 'backend-repo',
            path: '/workspace/backend',
          },
        ],
      };

      workspaceStore.getActiveWorkspace.mockReturnValue(mockWorkspace as any);
      
      // Create workspace directories BEFORE running test
      await mockFs.mkdir('/workspace', { recursive: true });
      await mockFs.mkdir('/workspace/backend', { recursive: true });

      const args = {
        tddMode: 'moderate' as const,
        repository: 'backend-repo',
      };

      const result = await toolHandler({
        params: {
          name: 'generate_claude_config',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('✅ CLAUDE.md created for repository "backend-repo"!');
      expect(result.structured.repository).toBe('backend-repo');
      
      // Check file was written
      expect(mockFs.hasFile('/workspace/backend/CLAUDE.md')).toBe(true);
      const content = mockFs.getFileContent('/workspace/backend/CLAUDE.md');
      expect(content).toContain('**Mode**: moderate');
    });
  });

  describe('create_documentation', () => {
    it('should create API documentation', async () => {
      const args = {
        type: 'api' as const,
        title: 'REST API',
        content: 'API endpoints documentation...',
      };

      const result = await toolHandler({
        params: {
          name: 'create_documentation',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('✅ Documentation created: API.md');
      expect(result.structured.type).toBe('api');
      // Check file was written
      expect(mockFs.hasFile('/project/root/docs/API.md')).toBe(true);
      const content = mockFs.getFileContent('/project/root/docs/API.md');
      expect(content).toBe('# REST API\n\nAPI endpoints documentation...');
    });

    it('should create custom documentation with repository context', async () => {
      const mockWorkspace = {
        name: 'Test Workspace',
        repositories: [
          {
            name: 'mobile-repo',
            path: '/workspace/mobile',
          },
        ],
      };

      workspaceStore.getActiveWorkspace.mockReturnValue(mockWorkspace as any);
      
      // Create workspace directories BEFORE running test
      await mockFs.mkdir('/workspace', { recursive: true });
      await mockFs.mkdir('/workspace/mobile', { recursive: true });
      await mockFs.mkdir('/workspace/mobile/docs', { recursive: true });

      const args = {
        type: 'custom' as const,
        title: 'Mobile Setup Guide',
        content: 'How to set up the mobile development environment...',
        repository: 'mobile-repo',
      };

      const result = await toolHandler({
        params: {
          name: 'create_documentation',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('✅ Documentation created: mobile-setup-guide.md for repository "mobile-repo"');
      expect(result.structured.repository).toBe('mobile-repo');
      
      // Check file was written
      expect(mockFs.hasFile('/workspace/mobile/docs/mobile-setup-guide.md')).toBe(true);
      const content = mockFs.getFileContent('/workspace/mobile/docs/mobile-setup-guide.md');
      expect(content).toContain('# Mobile Setup Guide');
    });
  });

  describe('list_repositories_docs', () => {
    it('should list documentation across repositories', async () => {
      const mockWorkspace = {
        name: 'Test Workspace',
        repositories: [
          {
            name: 'frontend',
            path: '/workspace/frontend',
            primary: true,
          },
          {
            name: 'backend',
            path: '/workspace/backend',
          },
        ],
      };

      workspaceStore.getActiveWorkspace.mockReturnValue(mockWorkspace as any);

      // Create workspace directories and files
      await mockFs.mkdir('/workspace', { recursive: true });
      await mockFs.mkdir('/workspace/frontend', { recursive: true });
      await mockFs.mkdir('/workspace/frontend/docs', { recursive: true });
      await mockFs.mkdir('/workspace/backend', { recursive: true });
      
      // Create mock files
      await mockFs.writeFile('/workspace/frontend/README.md', 'Frontend README', 'utf-8');
      await mockFs.writeFile('/workspace/backend/CLAUDE.md', 'Backend CLAUDE config', 'utf-8');
      await mockFs.writeFile('/workspace/frontend/docs/api.md', 'API docs', 'utf-8');
      await mockFs.writeFile('/workspace/frontend/docs/guide.md', 'Guide docs', 'utf-8');

      const result = await toolHandler({
        params: {
          name: 'list_repositories_docs',
          arguments: {},
        },
      });

      expect(result.content[0].text).toContain('📚 Documentation across repositories in "Test Workspace":');
      expect(result.content[0].text).toContain('📦 frontend (Primary)');
      expect(result.content[0].text).toContain('📦 backend');
      expect(result.structured.repositories).toHaveLength(2);
    });

    it('should handle workspace not found', async () => {
      workspaceStore.getActiveWorkspace.mockReturnValue(null);

      const result = await toolHandler({
        params: {
          name: 'list_repositories_docs',
          arguments: {},
        },
      });

      expect(result.content[0].text).toContain('❌ No workspace found');
    });
  });

  describe('generate_cross_repo_readme', () => {
    it('should generate workspace-level README', async () => {
      const mockWorkspace = {
        name: 'Multi-Service Platform',
        rootPath: '/workspace',
        repositories: [
          {
            name: 'api-service',
            path: '/workspace/api-service',
            primary: true,
            remote: 'https://github.com/company/api-service.git',
          },
          {
            name: 'web-app',
            path: '/workspace/web-app',
            remote: 'https://github.com/company/web-app.git',
          },
        ],
      };

      workspaceStore.getActiveWorkspace.mockReturnValue(mockWorkspace as any);
      
      // Create workspace directory BEFORE running test
      await mockFs.mkdir('/workspace', { recursive: true });

      const args = {
        projectName: 'Platform Suite',
        description: 'Complete business platform',
      };

      const result = await toolHandler({
        params: {
          name: 'generate_cross_repo_readme',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('✅ Cross-repository README.md created for workspace "Multi-Service Platform"!');
      expect(result.structured.repositories).toHaveLength(2);
      
      // Check file was written
      expect(mockFs.hasFile('/workspace/README.md')).toBe(true);
      const content = mockFs.getFileContent('/workspace/README.md');
      expect(content).toContain('# Platform Suite');
    });
  });

  describe('sync_docs_across_repos', () => {
    it('should sync documentation from source to target repositories', async () => {
      const mockWorkspace = {
        name: 'Test Workspace',
        repositories: [
          {
            name: 'main-repo',
            path: '/workspace/main',
          },
          {
            name: 'service-a',
            path: '/workspace/service-a',
          },
          {
            name: 'service-b',
            path: '/workspace/service-b',
          },
        ],
      };

      workspaceStore.getActiveWorkspace.mockReturnValue(mockWorkspace as any);

      // Create workspace directories and source file
      await mockFs.mkdir('/workspace', { recursive: true });
      await mockFs.mkdir('/workspace/main', { recursive: true });
      await mockFs.mkdir('/workspace/main/docs', { recursive: true });
      await mockFs.mkdir('/workspace/service-a', { recursive: true });
      await mockFs.mkdir('/workspace/service-a/docs', { recursive: true });
      await mockFs.mkdir('/workspace/service-b', { recursive: true });
      await mockFs.mkdir('/workspace/service-b/docs', { recursive: true });
      
      const sourceContent = '# Contributing\n\nPlease follow these guidelines for main-repo...';
      await mockFs.writeFile('/workspace/main/docs/CONTRIBUTING.md', sourceContent, 'utf-8');

      const args = {
        sourceRepository: 'main-repo',
        docType: 'contributing' as const,
        targetRepositories: ['service-a', 'service-b'],
      };

      const result = await toolHandler({
        params: {
          name: 'sync_docs_across_repos',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('✅ Documentation synced across repositories!');
      expect(result.structured.syncedRepositories).toHaveLength(2);
      
      // Check that content was modified for each repository
      expect(mockFs.hasFile('/workspace/service-a/docs/CONTRIBUTING.md')).toBe(true);
      const contentA = mockFs.getFileContent('/workspace/service-a/docs/CONTRIBUTING.md');
      expect(contentA).toContain('service-a');
      
      expect(mockFs.hasFile('/workspace/service-b/docs/CONTRIBUTING.md')).toBe(true);
      const contentB = mockFs.getFileContent('/workspace/service-b/docs/CONTRIBUTING.md');
      expect(contentB).toContain('service-b');
    });

    it('should handle missing source document', async () => {
      const mockWorkspace = {
        repositories: [
          { name: 'main-repo', path: '/workspace/main' },
        ],
      };

      workspaceStore.getActiveWorkspace.mockReturnValue(mockWorkspace as any);
      
      // Create workspace directories but no source file
      await mockFs.mkdir('/workspace', { recursive: true });
      await mockFs.mkdir('/workspace/main', { recursive: true });
      await mockFs.mkdir('/workspace/main/docs', { recursive: true });

      const args = {
        sourceRepository: 'main-repo',
        docType: 'contributing' as const,
      };

      const result = await toolHandler({
        params: {
          name: 'sync_docs_across_repos',
          arguments: args,
        },
      });

      expect(result.content[0].text).toContain('❌ Source document not found');
    });
  });

  describe('error handling', () => {
    it('should handle unknown tool names', async () => {
      const result = await toolHandler({
        params: {
          name: 'unknown_tool',
          arguments: {},
        },
      });

      expect(result).toBeNull();
    });

    it('should handle missing arguments', async () => {
      await expect(toolHandler({
        params: {
          name: 'generate_readme',
          arguments: null,
        },
      })).rejects.toThrow('No arguments provided');
    });

    it('should handle file system errors', async () => {
      // Remove the root directory to simulate permission error
      mockFs.clear();

      const args = {
        projectName: 'Test Project',
        description: 'A test project',
      };

      await expect(toolHandler({
        params: {
          name: 'generate_readme',
          arguments: args,
        },
      })).rejects.toThrow();
    });
  });
});