import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { ConfigManager } from '../config-manager.js';
import { AtlasConfig } from '../types.js';

describe('ConfigManager', () => {
  let tempDir: string;
  let configManager: ConfigManager;

  beforeEach(async () => {
    // Create a temporary directory for testing
    tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'atlas-test-'));
    process.chdir(tempDir);
  });

  afterEach(async () => {
    // Clean up
    process.chdir('/');
    await fs.rm(tempDir, { recursive: true, force: true });
  });

  describe('Configuration Loading', () => {
    it('should use default config when no config file exists', async () => {
      configManager = new ConfigManager();
      await configManager.load();
      
      const config = configManager.get();
      expect(config.projectMode).toBe('new');
      expect(config.features?.tddEnforcement).toBe('strict');
      expect(config.storage?.mode).toBe('local');
    });

    it('should load config from atlas.config.json', async () => {
      const customConfig: Partial<AtlasConfig> = {
        projectMode: 'existing',
        projectName: 'test-project',
        features: {
          tddEnforcement: 'advisory',
        },
      };
      
      await fs.writeFile(
        path.join(tempDir, 'atlas.config.json'),
        JSON.stringify(customConfig)
      );
      
      configManager = new ConfigManager();
      await configManager.load();
      
      const config = configManager.get();
      expect(config.projectMode).toBe('existing');
      expect(config.projectName).toBe('test-project');
      expect(config.features?.tddEnforcement).toBe('advisory');
    });

    it('should merge custom config with defaults', async () => {
      const customConfig: Partial<AtlasConfig> = {
        projectMode: 'multi-repo',
      };
      
      await fs.writeFile(
        path.join(tempDir, 'atlas.config.json'),
        JSON.stringify(customConfig)
      );
      
      configManager = new ConfigManager();
      await configManager.load();
      
      const config = configManager.get();
      expect(config.projectMode).toBe('multi-repo');
      expect(config.features?.tddEnforcement).toBe('strict'); // Default value
    });
  });

  describe('Project Detection', () => {
    it('should detect a new project', async () => {
      configManager = new ConfigManager();
      const detection = await configManager.detectProject();
      
      expect(detection.hasGit).toBe(false);
      expect(detection.hasPackageJson).toBe(false);
      expect(detection.suggestedMode).toBe('new');
    });

    it('should detect an existing Node.js project', async () => {
      // Create git directory
      await fs.mkdir(path.join(tempDir, '.git'));
      
      // Create package.json
      await fs.writeFile(
        path.join(tempDir, 'package.json'),
        JSON.stringify({
          name: 'test-project',
          devDependencies: {
            jest: '^29.0.0',
          },
        })
      );
      
      configManager = new ConfigManager();
      const detection = await configManager.detectProject();
      
      expect(detection.hasGit).toBe(true);
      expect(detection.hasPackageJson).toBe(true);
      expect(detection.hasTests).toBe(true);
      expect(detection.testFramework).toBe('jest');
      expect(detection.suggestedMode).toBe('existing');
    });

    it('should detect a multi-repo project', async () => {
      // Create multiple subdirectories with package.json files
      await fs.mkdir(path.join(tempDir, 'frontend'));
      await fs.mkdir(path.join(tempDir, 'backend'));
      
      await fs.writeFile(
        path.join(tempDir, 'frontend', 'package.json'),
        JSON.stringify({ name: 'frontend' })
      );
      
      await fs.writeFile(
        path.join(tempDir, 'backend', 'package.json'),
        JSON.stringify({ name: 'backend' })
      );
      
      configManager = new ConfigManager();
      const detection = await configManager.detectProject();
      
      expect(detection.suggestedMode).toBe('multi-repo');
    });
  });

  describe('Storage Path Resolution', () => {
    it('should use local storage by default', async () => {
      configManager = new ConfigManager();
      await configManager.load();
      
      const storagePath = configManager.getDataPath();
      expect(storagePath).toContain('.atlas');
      expect(storagePath.endsWith('.atlas')).toBe(true);
    });

    it('should use home directory storage when configured', async () => {
      configManager = new ConfigManager();
      configManager.set({
        projectName: 'test-project',
        storage: {
          mode: 'home',
          isolation: 'project',
        },
      });
      
      const storagePath = configManager.getDataPath();
      expect(storagePath).toBe(
        path.join(os.homedir(), '.atlas', 'projects', 'test-project')
      );
    });

    it('should use custom storage path when configured', async () => {
      const customPath = '/custom/storage/path';
      configManager = new ConfigManager();
      configManager.set({
        storage: {
          mode: 'custom',
          path: customPath,
        },
      });
      
      const storagePath = configManager.getDataPath();
      expect(storagePath).toBe(customPath);
    });
  });

  describe('Module Management', () => {
    it('should enable all modules by default', async () => {
      configManager = new ConfigManager();
      await configManager.load();
      
      expect(configManager.isModuleEnabled('kanban')).toBe(true);
      expect(configManager.isModuleEnabled('development')).toBe(true);
      expect(configManager.isModuleEnabled('documentation')).toBe(true);
    });

    it('should respect module configuration', async () => {
      configManager = new ConfigManager();
      configManager.set({
        integration: {
          modules: {
            kanban: true,
            development: false,
            documentation: true,
          },
        },
      });
      
      expect(configManager.isModuleEnabled('kanban')).toBe(true);
      expect(configManager.isModuleEnabled('development')).toBe(false);
      expect(configManager.isModuleEnabled('documentation')).toBe(true);
    });
  });

  describe('Configuration Persistence', () => {
    it('should save configuration to file', async () => {
      configManager = new ConfigManager();
      configManager.set({
        projectMode: 'existing',
        projectName: 'saved-project',
      });
      
      await configManager.save();
      
      // Verify file was created
      const savedConfig = JSON.parse(
        await fs.readFile(path.join(tempDir, 'atlas.config.json'), 'utf-8')
      );
      
      expect(savedConfig.projectMode).toBe('existing');
      expect(savedConfig.projectName).toBe('saved-project');
    });
  });
});