/**
 * Handler Registry Implementation
 * Manages handler metadata and configuration without circular dependencies
 */

import type { JobHandler, ScheduledJob } from '@stock-bot/types';
import type { HandlerConfiguration, HandlerMetadata, RegistryStats } from './types';

export class HandlerRegistry {
  private handlers = new Map<string, HandlerMetadata>();
  private configurations = new Map<string, HandlerConfiguration>();
  private handlerServices = new Map<string, string>();

  /**
   * Register handler metadata
   */
  registerMetadata(metadata: HandlerMetadata): void {
    this.handlers.set(metadata.name, metadata);

    if (metadata.service) {
      this.handlerServices.set(metadata.name, metadata.service);
    }
  }

  /**
   * Register handler configuration with operation implementations
   */
  registerConfiguration(config: HandlerConfiguration): void {
    this.configurations.set(config.name, config);
  }

  /**
   * Register both metadata and configuration
   */
  register(metadata: HandlerMetadata, config: HandlerConfiguration): void {
    this.registerMetadata(metadata);
    this.registerConfiguration(config);
  }

  /**
   * Get handler metadata
   */
  getMetadata(handlerName: string): HandlerMetadata | undefined {
    return this.handlers.get(handlerName);
  }

  /**
   * Get handler configuration
   */
  getConfiguration(handlerName: string): HandlerConfiguration | undefined {
    return this.configurations.get(handlerName);
  }

  /**
   * Get a specific operation handler
   */
  getOperation(handlerName: string, operationName: string): JobHandler | undefined {
    const config = this.configurations.get(handlerName);
    return config?.operations[operationName];
  }

  /**
   * Get all handler metadata
   */
  getAllMetadata(): Map<string, HandlerMetadata> {
    return new Map(this.handlers);
  }

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

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

  /**
   * Set service ownership for a handler
   */
  setHandlerService(handlerName: string, serviceName: string): void {
    this.handlerServices.set(handlerName, serviceName);

    // Update metadata if it exists
    const metadata = this.handlers.get(handlerName);
    if (metadata) {
      metadata.service = serviceName;
    }
  }

  /**
   * Get the service that owns a handler
   */
  getHandlerService(handlerName: string): string | undefined {
    return this.handlerServices.get(handlerName);
  }

  /**
   * Get all handlers for a specific service
   */
  getServiceHandlers(serviceName: string): HandlerMetadata[] {
    const handlers: HandlerMetadata[] = [];
    for (const [handlerName, metadata] of this.handlers) {
      if (
        this.handlerServices.get(handlerName) === serviceName ||
        metadata.service === serviceName
      ) {
        handlers.push(metadata);
      }
    }
    return handlers;
  }

  /**
   * Get scheduled jobs for a handler
   */
  getScheduledJobs(handlerName: string): ScheduledJob[] {
    const config = this.configurations.get(handlerName);
    return config?.scheduledJobs || [];
  }

  /**
   * Get all handlers with their scheduled jobs
   */
  getAllHandlersWithSchedule(): Map<
    string,
    { metadata: HandlerMetadata; scheduledJobs: ScheduledJob[] }
  > {
    const result = new Map<string, { metadata: HandlerMetadata; scheduledJobs: ScheduledJob[] }>();

    for (const [name, metadata] of this.handlers) {
      const config = this.configurations.get(name);
      result.set(name, {
        metadata,
        scheduledJobs: config?.scheduledJobs || [],
      });
    }

    return result;
  }

  /**
   * Get registry statistics
   */
  getStats(): RegistryStats {
    let operationCount = 0;
    let scheduledJobCount = 0;
    const services = new Set<string>();

    for (const metadata of this.handlers.values()) {
      operationCount += metadata.operations.length;
      scheduledJobCount += metadata.schedules?.length || 0;

      if (metadata.service) {
        services.add(metadata.service);
      }
    }

    return {
      handlers: this.handlers.size,
      operations: operationCount,
      scheduledJobs: scheduledJobCount,
      services: services.size,
    };
  }

  /**
   * Clear all registrations (useful for testing)
   */
  clear(): void {
    this.handlers.clear();
    this.configurations.clear();
    this.handlerServices.clear();
  }

  /**
   * Export registry data for debugging or persistence
   */
  export(): {
    handlers: Array<[string, HandlerMetadata]>;
    configurations: Array<[string, HandlerConfiguration]>;
    services: Array<[string, string]>;
  } {
    return {
      handlers: Array.from(this.handlers.entries()),
      configurations: Array.from(this.configurations.entries()),
      services: Array.from(this.handlerServices.entries()),
    };
  }

  /**
   * Import registry data
   */
  import(data: {
    handlers: Array<[string, HandlerMetadata]>;
    configurations: Array<[string, HandlerConfiguration]>;
    services: Array<[string, string]>;
  }): void {
    this.clear();

    for (const [name, metadata] of data.handlers) {
      this.handlers.set(name, metadata);
    }

    for (const [name, config] of data.configurations) {
      this.configurations.set(name, config);
    }

    for (const [handler, service] of data.services) {
      this.handlerServices.set(handler, service);
    }
  }
}
