import { describe, it, expect, beforeEach, vi } from 'vitest';
import { MethodologyManager } from '../methodology-manager.js';
import { ConfigManager } from '../../../config/config-manager.js';
import { AGILE_TOOLS, KANBAN_TOOLS, COMMON_TOOLS } from '../types.js';

vi.mock('../../../config/config-manager.js');

describe('MethodologyManager', () => {
  let methodologyManager: MethodologyManager;
  let mockConfigManager: vi.Mocked<ConfigManager>;

  beforeEach(async () => {
    mockConfigManager = new ConfigManager() as vi.Mocked<ConfigManager>;
    mockConfigManager.getStorageManager = vi.fn().mockReturnValue({
      loadData: vi.fn().mockResolvedValue(null),
      saveData: vi.fn().mockResolvedValue(undefined),
      ensureStorageDirectories: vi.fn().mockResolvedValue(undefined)
    });

    methodologyManager = new MethodologyManager(mockConfigManager);
    await methodologyManager.init();
  });

  describe('setMethodology', () => {
    it('should set agile methodology with correct tools', async () => {
      const methodology = await methodologyManager.setMethodology('agile', 'user123');

      expect(methodology.type).toBe('agile');
      expect(methodology.allowedTools).toContain(...COMMON_TOOLS);
      expect(methodology.allowedTools).toContain(...AGILE_TOOLS);
      expect(methodology.disallowedTools).toEqual(KANBAN_TOOLS);
    });

    it('should set kanban methodology with correct tools', async () => {
      const methodology = await methodologyManager.setMethodology('kanban', 'user123');

      expect(methodology.type).toBe('kanban');
      expect(methodology.allowedTools).toContain(...COMMON_TOOLS);
      expect(methodology.allowedTools).toContain(...KANBAN_TOOLS);
      expect(methodology.disallowedTools).toEqual(AGILE_TOOLS);
    });

    it('should record migration when switching methodologies', async () => {
      // Set initial methodology
      await methodologyManager.setMethodology('agile', 'user123');
      
      // Switch to kanban
      const methodology = await methodologyManager.setMethodology('kanban', 'user123');

      expect(methodology.migrationHistory).toHaveLength(1);
      const migration = methodology.migrationHistory[0];
      expect(migration.fromType).toBe('agile');
      expect(migration.toType).toBe('kanban');
      expect(migration.migratedBy).toBe('user123');
      expect(migration.dataLost).toContain('Sprint information will be lost');
    });

    it('should emit methodology-changed event', async () => {
      let eventEmitted = false;
      methodologyManager.once('methodology-changed', (methodology) => {
        eventEmitted = true;
        expect(methodology.type).toBe('agile');
      });

      await methodologyManager.setMethodology('agile', 'user123');
      expect(eventEmitted).toBe(true);
    });
  });

  describe('lockMethodology', () => {
    it('should lock methodology preventing changes', async () => {
      await methodologyManager.setMethodology('agile', 'user123');
      await methodologyManager.lockMethodology('admin');

      // Try to change methodology
      await expect(
        methodologyManager.setMethodology('kanban', 'user456')
      ).rejects.toThrow('Methodology is locked');
    });

    it('should record who locked the methodology', async () => {
      await methodologyManager.setMethodology('agile', 'user123');
      await methodologyManager.lockMethodology('admin');

      const methodology = methodologyManager.getCurrentMethodology();
      expect(methodology?.lockedBy).toBe('admin');
      expect(methodology?.lockedAt).toBeDefined();
    });

    it('should not allow locking twice', async () => {
      await methodologyManager.setMethodology('agile', 'user123');
      await methodologyManager.lockMethodology('admin');

      await expect(
        methodologyManager.lockMethodology('admin2')
      ).rejects.toThrow('Methodology is already locked');
    });

    it('should emit methodology-locked event', async () => {
      await methodologyManager.setMethodology('agile', 'user123');
      
      let eventEmitted = false;
      methodologyManager.once('methodology-locked', (methodology) => {
        eventEmitted = true;
        expect(methodology.type).toBe('agile');
      });

      await methodologyManager.lockMethodology('admin');
      expect(eventEmitted).toBe(true);
    });
  });

  describe('isToolAllowed', () => {
    it('should allow common tools in both methodologies', async () => {
      await methodologyManager.setMethodology('agile', 'user123');
      
      for (const tool of COMMON_TOOLS) {
        expect(methodologyManager.isToolAllowed(tool)).toBe(true);
      }
    });

    it('should allow agile tools only in agile methodology', async () => {
      await methodologyManager.setMethodology('agile', 'user123');
      
      for (const tool of AGILE_TOOLS) {
        expect(methodologyManager.isToolAllowed(tool)).toBe(true);
      }
      
      for (const tool of KANBAN_TOOLS) {
        expect(methodologyManager.isToolAllowed(tool)).toBe(false);
      }
    });

    it('should allow kanban tools only in kanban methodology', async () => {
      await methodologyManager.setMethodology('kanban', 'user123');
      
      for (const tool of KANBAN_TOOLS) {
        expect(methodologyManager.isToolAllowed(tool)).toBe(true);
      }
      
      for (const tool of AGILE_TOOLS) {
        expect(methodologyManager.isToolAllowed(tool)).toBe(false);
      }
    });

    it('should allow all tools when no methodology is set', () => {
      expect(methodologyManager.isToolAllowed('any-tool')).toBe(true);
    });
  });

  describe('warnAboutConflict', () => {
    it('should emit warning for conflicting tool usage', async () => {
      await methodologyManager.setMethodology('agile', 'user123');
      
      let warningEmitted = false;
      methodologyManager.once('methodology-conflict-warning', (warning) => {
        warningEmitted = true;
        expect(warning.toolName).toBe('create_board');
        expect(warning.currentMethodology).toBe('agile');
      });

      const shouldWarn = await methodologyManager.warnAboutConflict('create_board');
      expect(shouldWarn).toBe(true);
      expect(warningEmitted).toBe(true);
    });

    it('should throttle warnings to once per hour', async () => {
      await methodologyManager.setMethodology('agile', 'user123');
      
      const firstWarn = await methodologyManager.warnAboutConflict('create_board');
      expect(firstWarn).toBe(true);
      
      const secondWarn = await methodologyManager.warnAboutConflict('create_board');
      expect(secondWarn).toBe(false);
    });
  });

  describe('suggestMigrationPath', () => {
    it('should suggest steps for agile to kanban migration', async () => {
      const suggestions = await methodologyManager.suggestMigrationPath('agile', 'kanban');
      
      expect(suggestions).toContain('1. Complete or cancel active sprints');
      expect(suggestions).toContain('2. Convert stories to tasks');
      expect(suggestions).toContain('3. Set up WIP limits for columns');
    });

    it('should suggest steps for kanban to agile migration', async () => {
      const suggestions = await methodologyManager.suggestMigrationPath('kanban', 'agile');
      
      expect(suggestions).toContain('1. Group related tasks into stories');
      expect(suggestions).toContain('2. Estimate story points for all items');
      expect(suggestions).toContain('3. Create initial sprint backlog');
    });
  });

  describe('Default configurations', () => {
    it('should set appropriate defaults for agile', async () => {
      const methodology = await methodologyManager.setMethodology('agile', 'user123');
      
      expect(methodology.configuration.sprintDuration).toBe(14);
      expect(methodology.configuration.velocityTracking).toBe(true);
      expect(methodology.configuration.storyPointScale).toEqual([1, 2, 3, 5, 8, 13, 21]);
      expect(methodology.configuration.requiredFields).toContain('storyPoints');
      expect(methodology.configuration.requiredFields).toContain('acceptanceCriteria');
    });

    it('should set appropriate defaults for kanban', async () => {
      const methodology = await methodologyManager.setMethodology('kanban', 'user123');
      
      expect(methodology.configuration.wip_limits).toBeDefined();
      expect(methodology.configuration.wip_limits['In Progress']).toBe(3);
      expect(methodology.configuration.cycleTimeTracking).toBe(true);
      expect(methodology.configuration.leadTimeTracking).toBe(true);
      expect(methodology.configuration.requiredFields).not.toContain('storyPoints');
    });
  });
});