import { getLogger } from '@stock-bot/logger';
import type {
  HandlerConfig,
  HandlerConfigWithSchedule,
  JobHandler,
  ScheduledJob,
} from '../types/types';

const logger = getLogger('handler-registry');

class HandlerRegistry {
  private handlers = new Map<string, HandlerConfig>();
  private handlerSchedules = new Map<string, ScheduledJob[]>();

  /**
   * Register a handler with its operations (simple config)
   */
  register(handlerName: string, config: HandlerConfig): void {
    logger.info(`Registering handler: ${handlerName}`, {
      operations: Object.keys(config),
    });

    this.handlers.set(handlerName, config);
  }

  /**
   * Register a handler with operations and scheduled jobs (full config)
   */
  registerWithSchedule(config: HandlerConfigWithSchedule): void {
    logger.info(`Registering handler with schedule: ${config.name}`, {
      operations: Object.keys(config.operations),
      scheduledJobs: config.scheduledJobs?.length || 0,
    });

    this.handlers.set(config.name, config.operations);

    if (config.scheduledJobs && config.scheduledJobs.length > 0) {
      this.handlerSchedules.set(config.name, config.scheduledJobs);
    }
  }

  /**
   * Get a handler for a specific handler and operation
   */
  getHandler(handler: string, operation: string): JobHandler | null {
    const handlerConfig = this.handlers.get(handler);
    if (!handlerConfig) {
      logger.warn(`Handler not found: ${handler}`);
      return null;
    }

    const jobHandler = handlerConfig[operation];
    if (!jobHandler) {
      logger.warn(`Operation not found: ${handler}:${operation}`, {
        availableOperations: Object.keys(handlerConfig),
      });
      return null;
    }

    return jobHandler;
  }

  /**
   * Get all scheduled jobs from all handlers
   */
  getAllScheduledJobs(): Array<{ handler: string; job: ScheduledJob }> {
    const allJobs: Array<{ handler: string; job: ScheduledJob }> = [];

    for (const [handlerName, jobs] of this.handlerSchedules) {
      for (const job of jobs) {
        allJobs.push({
          handler: handlerName,
          job,
        });
      }
    }

    return allJobs;
  }

  /**
   * Get scheduled jobs for a specific handler
   */
  getScheduledJobs(handler: string): ScheduledJob[] {
    return this.handlerSchedules.get(handler) || [];
  }

  /**
   * Check if a handler has scheduled jobs
   */
  hasScheduledJobs(handler: string): boolean {
    return this.handlerSchedules.has(handler);
  }

  /**
   * Get all registered handlers with their configurations
   */
  getHandlerConfigs(): Array<{ name: string; operations: string[]; scheduledJobs: number }> {
    return Array.from(this.handlers.keys()).map(name => ({
      name,
      operations: Object.keys(this.handlers.get(name) || {}),
      scheduledJobs: this.handlerSchedules.get(name)?.length || 0,
    }));
  }

  /**
   * Get all handlers with their full configurations for queue manager registration
   */
  getAllHandlers(): Map<string, { operations: HandlerConfig; scheduledJobs?: ScheduledJob[] }> {
    const result = new Map<string, { operations: HandlerConfig; scheduledJobs?: ScheduledJob[] }>();

    for (const [name, operations] of this.handlers) {
      const scheduledJobs = this.handlerSchedules.get(name);
      result.set(name, {
        operations,
        scheduledJobs,
      });
    }

    return result;
  }

  /**
   * Get all registered handlers
   */
  getHandlers(): string[] {
    return Array.from(this.handlers.keys());
  }

  /**
   * Get operations for a specific handler
   */
  getOperations(handler: string): string[] {
    const handlerConfig = this.handlers.get(handler);
    return handlerConfig ? Object.keys(handlerConfig) : [];
  }

  /**
   * Check if a handler exists
   */
  hasHandler(handler: string): boolean {
    return this.handlers.has(handler);
  }

  /**
   * Check if a handler has a specific operation
   */
  hasOperation(handler: string, operation: string): boolean {
    const handlerConfig = this.handlers.get(handler);
    return handlerConfig ? operation in handlerConfig : false;
  }

  /**
   * Remove a handler
   */
  unregister(handler: string): boolean {
    this.handlerSchedules.delete(handler);
    return this.handlers.delete(handler);
  }

  /**
   * Clear all handlers
   */
  clear(): void {
    this.handlers.clear();
    this.handlerSchedules.clear();
  }

  /**
   * Get registry statistics
   */
  getStats(): { handlers: number; totalOperations: number; totalScheduledJobs: number } {
    let totalOperations = 0;
    let totalScheduledJobs = 0;

    for (const config of this.handlers.values()) {
      totalOperations += Object.keys(config).length;
    }

    for (const jobs of this.handlerSchedules.values()) {
      totalScheduledJobs += jobs.length;
    }

    return {
      handlers: this.handlers.size,
      totalOperations,
      totalScheduledJobs,
    };
  }
}

// Export singleton instance
export const handlerRegistry = new HandlerRegistry();
