/**
 * Lazy Loading Generator Registry
 * Loads generators on-demand to improve startup performance
 */

import { Generator } from './types';
import { GeneratorRegistry } from './generator-registry';

/**
 * Generator factory function for lazy initialization
 */
type GeneratorFactory = () => Generator | Promise<Generator>;

/**
 * Generator loader configuration
 */
interface GeneratorLoader {
  id: string;
  category: 'frontend' | 'backend' | 'database' | 'test' | 'documentation' | 'cli' | 'custom';
  factory: GeneratorFactory;
  loaded: boolean;
  instance?: Generator;
}

/**
 * Lazy-loading wrapper for GeneratorRegistry
 * Only loads generators when they're actually needed
 */
export class LazyGeneratorRegistry {
  private registry: GeneratorRegistry;
  private loaders: Map<string, GeneratorLoader> = new Map();
  private loadTimes: Map<string, number> = new Map();

  constructor(baseRegistry: GeneratorRegistry) {
    this.registry = baseRegistry;
  }

  /**
   * Register a generator with lazy loading
   */
  registerLazy(
    id: string,
    category: GeneratorLoader['category'],
    factory: GeneratorFactory
  ): void {
    this.loaders.set(id, {
      id,
      category,
      factory,
      loaded: false,
    });
  }

  /**
   * Get a generator by ID, loading it if necessary
   */
  async getGenerator(id: string): Promise<Generator | undefined> {
    const loader = this.loaders.get(id);

    if (!loader) {
      // Try custom generators from base registry
      return this.registry.getCustomGenerator(id);
    }

    // Load generator if not already loaded
    if (!loader.loaded) {
      await this.loadGenerator(loader);
    }

    return loader.instance;
  }

  /**
   * Get frontend generator (lazy load)
   */
  async getFrontendGenerator(framework: string): Promise<Generator | undefined> {
    // Try to find in lazy loaders first
    for (const loader of this.loaders.values()) {
      if (loader.category === 'frontend') {
        if (!loader.loaded) {
          await this.loadGenerator(loader);
        }
        if (loader.instance?.id === framework || (loader.instance as any).framework === framework) {
          return loader.instance;
        }
      }
    }

    // Fall back to base registry
    return this.registry.getFrontendGenerator(framework);
  }

  /**
   * Get backend generator (lazy load)
   */
  async getBackendGenerator(language?: string, framework?: string): Promise<Generator | undefined> {
    // Try to find in lazy loaders first
    for (const loader of this.loaders.values()) {
      if (loader.category === 'backend') {
        if (!loader.loaded) {
          await this.loadGenerator(loader);
        }
        const gen = loader.instance;
        if (gen && this.matchesBackendCriteria(gen, language, framework)) {
          return gen;
        }
      }
    }

    // Fall back to base registry
    if (!language) {
      return undefined;
    }
    return this.registry.getBackendGenerator(language, framework);
  }

  /**
   * Get database generator (lazy load)
   */
  async getDatabaseGenerator(technology: string): Promise<Generator | undefined> {
    // Try to find in lazy loaders first
    for (const loader of this.loaders.values()) {
      if (loader.category === 'database') {
        if (!loader.loaded) {
          await this.loadGenerator(loader);
        }
        if (loader.instance?.id === technology) {
          return loader.instance;
        }
      }
    }

    // Fall back to base registry
    return this.registry.getDatabaseGenerator(technology);
  }

  /**
   * Get test generator (lazy load)
   */
  async getTestGenerator(framework: string): Promise<Generator | undefined> {
    // Try to find in lazy loaders first
    for (const loader of this.loaders.values()) {
      if (loader.category === 'test') {
        if (!loader.loaded) {
          await this.loadGenerator(loader);
        }
        if (loader.instance?.id === framework) {
          return loader.instance;
        }
      }
    }

    // Fall back to base registry
    return this.registry.getTestGenerator(framework);
  }

  /**
   * Get all custom generators (lazy load all)
   */
  async getCustomGenerators(): Promise<Generator[]> {
    const generators: Generator[] = [];
    const seenIds = new Set<string>();

    // Load all custom generators from lazy loaders
    for (const loader of this.loaders.values()) {
      if (loader.category === 'custom') {
        if (!loader.loaded) {
          await this.loadGenerator(loader);
        }
        if (loader.instance) {
          generators.push(loader.instance);
          seenIds.add(loader.instance.id);
        }
      }
    }

    // Add from base registry (excluding already loaded ones)
    for (const gen of this.registry.getCustomGenerators()) {
      if (!seenIds.has(gen.id)) {
        generators.push(gen);
      }
    }

    return generators;
  }

  /**
   * Preload specific generators (for performance-critical paths)
   */
  async preload(ids: string[]): Promise<void> {
    const promises: Promise<void>[] = [];

    for (const id of ids) {
      const loader = this.loaders.get(id);
      if (loader && !loader.loaded) {
        promises.push(this.loadGenerator(loader));
      }
    }

    await Promise.all(promises);
  }

  /**
   * Preload all generators (useful for warming up)
   */
  async preloadAll(): Promise<void> {
    const promises: Promise<void>[] = [];

    for (const loader of this.loaders.values()) {
      if (!loader.loaded) {
        promises.push(this.loadGenerator(loader));
      }
    }

    await Promise.all(promises);
  }

  /**
   * Load a specific generator
   */
  private async loadGenerator(loader: GeneratorLoader): Promise<void> {
    if (loader.loaded) {
      return;
    }

    const startTime = Date.now();

    try {
      const instance = await Promise.resolve(loader.factory());
      loader.instance = instance;

      const loadTime = Date.now() - startTime;
      this.loadTimes.set(loader.id, loadTime);

      loader.loaded = true;

      // Register with base registry for compatibility
      // Use type assertions based on category since factory ensures correct type
      switch (loader.category) {
        case 'frontend':
          this.registry.registerFrontendGenerator(instance as any);
          break;
        case 'backend':
          this.registry.registerBackendGenerator(instance as any);
          break;
        case 'database':
          this.registry.registerDatabaseGenerator(instance as any);
          break;
        case 'test':
          this.registry.registerTestGenerator(instance as any);
          break;
        case 'documentation':
          this.registry.registerDocumentationGenerator(instance as any);
          break;
        case 'cli':
          this.registry.registerCliGenerator(instance as any);
          break;
        case 'custom':
          this.registry.registerCustomGenerator(instance);
          break;
      }
    } catch (error) {
      console.error(`Failed to load generator "${loader.id}":`, error);
      loader.loaded = true; // Mark as loaded to prevent retry loops
    }
  }

  /**
   * Check if generator matches backend criteria
   */
  private matchesBackendCriteria(
    generator: Generator,
    language?: string,
    framework?: string
  ): boolean {
    const gen = generator as any;

    if (language && gen.language !== language) {
      return false;
    }

    if (framework && gen.framework !== framework) {
      return false;
    }

    return true;
  }

  /**
   * Get loading statistics
   */
  getLoadStats(): {
    total: number;
    loaded: number;
    pending: number;
    totalLoadTime: number;
    averageLoadTime: number;
    loadTimes: Map<string, number>;
  } {
    const total = this.loaders.size;
    const loaded = Array.from(this.loaders.values()).filter(l => l.loaded).length;
    const pending = total - loaded;
    const totalLoadTime = Array.from(this.loadTimes.values()).reduce((sum, time) => sum + time, 0);
    const averageLoadTime = loaded > 0 ? totalLoadTime / loaded : 0;

    return {
      total,
      loaded,
      pending,
      totalLoadTime,
      averageLoadTime,
      loadTimes: new Map(this.loadTimes),
    };
  }

  /**
   * Check if a generator is loaded
   */
  isLoaded(id: string): boolean {
    const loader = this.loaders.get(id);
    return loader ? loader.loaded : false;
  }

  /**
   * Get list of all registered generator IDs
   */
  listGeneratorIds(): string[] {
    return Array.from(this.loaders.keys());
  }

  /**
   * Get list of loaded generator IDs
   */
  listLoadedGeneratorIds(): string[] {
    return Array.from(this.loaders.values())
      .filter(l => l.loaded)
      .map(l => l.id);
  }

  /**
   * Get list of pending generator IDs
   */
  listPendingGeneratorIds(): string[] {
    return Array.from(this.loaders.values())
      .filter(l => !l.loaded)
      .map(l => l.id);
  }

  /**
   * Get the underlying base registry (for compatibility)
   */
  getBaseRegistry(): GeneratorRegistry {
    return this.registry;
  }
}

/**
 * Create lazy registry with standard generators
 */
export function createLazyRegistry(baseRegistry: GeneratorRegistry): LazyGeneratorRegistry {
  const lazyRegistry = new LazyGeneratorRegistry(baseRegistry);

  // Register all standard generators with lazy loading
  lazyRegistry.registerLazy('react', 'frontend', async () => {
    const { ReactGenerator } = await import('./frontend/react-generator');
    return new ReactGenerator();
  });

  lazyRegistry.registerLazy('typescript-express', 'backend', async () => {
    const { TypeScriptExpressGenerator } = await import('./backend/typescript-express-generator');
    return new TypeScriptExpressGenerator();
  });

  lazyRegistry.registerLazy('typescript-cli', 'cli', async () => {
    const { TypeScriptCliGenerator } = await import('./cli/typescript-cli-generator');
    return new TypeScriptCliGenerator();
  });

  lazyRegistry.registerLazy('prisma', 'database', async () => {
    const { PrismaGenerator } = await import('./database/prisma-generator');
    return new PrismaGenerator();
  });

  lazyRegistry.registerLazy('drizzle', 'database', async () => {
    const { DrizzleGenerator } = await import('./database/drizzle-generator');
    return new DrizzleGenerator();
  });

  lazyRegistry.registerLazy('vitest', 'test', async () => {
    const { FrontendTestGenerator } = await import('./test/frontend-test-generator');
    return new FrontendTestGenerator();
  });

  lazyRegistry.registerLazy('jest', 'test', async () => {
    const { BackendTestGenerator } = await import('./test/backend-test-generator');
    return new BackendTestGenerator();
  });

  lazyRegistry.registerLazy('playwright', 'test', async () => {
    const { E2ETestGenerator } = await import('./test/e2e-test-generator');
    return new E2ETestGenerator();
  });

  lazyRegistry.registerLazy('cli-jest', 'test', async () => {
    const { CliTestGenerator } = await import('./test/cli-test-generator');
    return new CliTestGenerator();
  });

  lazyRegistry.registerLazy('markdown', 'documentation', async () => {
    const { MarkdownDocumentationGenerator } = await import('./docs/markdown-generator');
    return new MarkdownDocumentationGenerator();
  });

  lazyRegistry.registerLazy('github-actions', 'custom', async () => {
    const { GitHubActionsGenerator } = await import('./ci-cd/github-actions-generator');
    return new GitHubActionsGenerator();
  });

  lazyRegistry.registerLazy('environment', 'custom', async () => {
    const { EnvironmentGenerator } = await import('./env/environment-generator');
    return new EnvironmentGenerator();
  });

  lazyRegistry.registerLazy('service', 'custom', async () => {
    const { ServiceGenerator } = await import('./infrastructure/service-generator');
    return new ServiceGenerator();
  });

  lazyRegistry.registerLazy('upstash-redis', 'custom', async () => {
    const { UpstashRedisGenerator } = await import('./cache/upstash-redis-generator');
    return new UpstashRedisGenerator();
  });

  lazyRegistry.registerLazy('readme', 'custom', async () => {
    const { ReadmeGenerator } = await import('./readme-generator');
    return new ReadmeGenerator();
  });

  lazyRegistry.registerLazy('event-types', 'custom', async () => {
    const { generateEventTypes } = await import('./events/event-types-generator');
    return {
      id: 'event-types',
      name: 'Event Types Generator',
      description: 'Generates TypeScript event type interfaces from YAML event definitions',
      generate: generateEventTypes,
    };
  });

  lazyRegistry.registerLazy('event-bus', 'custom', async () => {
    const { generateEventBus } = await import('./events/event-bus-generator');
    return {
      id: 'event-bus',
      name: 'Event Bus Generator',
      description: 'Generates Redis pub/sub event bus infrastructure for event-driven architecture',
      generate: generateEventBus,
    };
  });

  lazyRegistry.registerLazy('event-handlers', 'custom', async () => {
    const { generateEventHandlers } = await import('./events/event-handlers-generator');
    return {
      id: 'event-handlers',
      name: 'Event Handlers Generator',
      description: 'Generates event handler implementations for consuming services',
      generate: generateEventHandlers,
    };
  });

  lazyRegistry.registerLazy('websocket-server', 'custom', async () => {
    const { generateWebSocketServer } = await import('./websocket/websocket-server-generator');
    return {
      id: 'websocket-server',
      name: 'WebSocket Server Generator',
      description: 'Generates WebSocket server infrastructure for real-time updates',
      generate: generateWebSocketServer,
    };
  });

  lazyRegistry.registerLazy('websocket-client', 'custom', async () => {
    const { generateWebSocketClient } = await import('./websocket/websocket-client-generator');
    return {
      id: 'websocket-client',
      name: 'WebSocket Client Generator',
      description: 'Generates React hooks and utilities for WebSocket connections',
      generate: generateWebSocketClient,
    };
  });

  return lazyRegistry;
}
