import * as plugins from './mod.plugins.js';
import { BaseFormatter } from './classes.baseformatter.js';

export interface IModuleDependency {
  module: string;
  dependencies: Set<string>;
  dependents: Set<string>;
}

export class DependencyAnalyzer {
  private moduleDependencies: Map<string, IModuleDependency> = new Map();
  
  constructor() {
    this.initializeDependencies();
  }
  
  private initializeDependencies(): void {
    // Define dependencies between format modules
    const dependencies = {
      'cleanup': [],  // No dependencies
      'npmextra': [],  // No dependencies
      'license': ['npmextra'],  // Depends on npmextra for config
      'packagejson': ['npmextra'],  // Depends on npmextra for config
      'templates': ['npmextra', 'packagejson'],  // Depends on both
      'gitignore': ['templates'],  // Depends on templates
      'tsconfig': ['packagejson'],  // Depends on package.json
      'prettier': ['cleanup', 'npmextra', 'packagejson', 'templates', 'gitignore', 'tsconfig'],  // Runs after most others
      'readme': ['npmextra', 'packagejson'],  // Depends on project metadata
      'copy': ['npmextra'],  // Depends on config
    };
    
    // Initialize all modules
    for (const [module, deps] of Object.entries(dependencies)) {
      this.moduleDependencies.set(module, {
        module,
        dependencies: new Set(deps),
        dependents: new Set()
      });
    }
    
    // Build reverse dependencies (dependents)
    for (const [module, deps] of Object.entries(dependencies)) {
      for (const dep of deps) {
        const depModule = this.moduleDependencies.get(dep);
        if (depModule) {
          depModule.dependents.add(module);
        }
      }
    }
  }
  
  getExecutionGroups(modules: BaseFormatter[]): BaseFormatter[][] {
    const modulesMap = new Map(modules.map(m => [m.name, m]));
    const executed = new Set<string>();
    const groups: BaseFormatter[][] = [];
    
    while (executed.size < modules.length) {
      const currentGroup: BaseFormatter[] = [];
      
      for (const module of modules) {
        if (executed.has(module.name)) continue;
        
        const dependency = this.moduleDependencies.get(module.name);
        if (!dependency) {
          // Unknown module, execute in isolation
          currentGroup.push(module);
          continue;
        }
        
        // Check if all dependencies have been executed
        const allDepsExecuted = Array.from(dependency.dependencies)
          .every(dep => executed.has(dep) || !modulesMap.has(dep));
        
        if (allDepsExecuted) {
          currentGroup.push(module);
        }
      }
      
      if (currentGroup.length === 0) {
        // Circular dependency or error - execute remaining modules
        for (const module of modules) {
          if (!executed.has(module.name)) {
            currentGroup.push(module);
          }
        }
      }
      
      currentGroup.forEach(m => executed.add(m.name));
      groups.push(currentGroup);
    }
    
    return groups;
  }
  
  canRunInParallel(module1: string, module2: string): boolean {
    const dep1 = this.moduleDependencies.get(module1);
    const dep2 = this.moduleDependencies.get(module2);
    
    if (!dep1 || !dep2) return false;
    
    // Check if module1 depends on module2 or vice versa
    return !dep1.dependencies.has(module2) && 
           !dep2.dependencies.has(module1) &&
           !dep1.dependents.has(module2) &&
           !dep2.dependents.has(module1);
  }
}