/**
 * Handler Scanner
 * Discovers and registers handlers with the DI container
 */

import { asFunction, type AwilixContainer } from 'awilix';
import { glob } from 'glob';
import type {
  HandlerConfiguration,
  HandlerMetadata,
  HandlerRegistry,
} from '@stock-bot/handler-registry';
import { getLogger } from '@stock-bot/logger';
import type { ExecutionContext, IHandler } from '@stock-bot/types';

export interface HandlerScannerOptions {
  serviceName?: string;
  autoRegister?: boolean;
  patterns?: string[];
}

export class HandlerScanner {
  private logger = getLogger('handler-scanner');
  private discoveredHandlers = new Map<string, any>();

  constructor(
    private registry: HandlerRegistry,
    private container: AwilixContainer,
    private options: HandlerScannerOptions = {}
  ) {}

  /**
   * Scan for handlers matching the given patterns
   */
  async scanHandlers(patterns: string[] = this.options.patterns || []): Promise<void> {
    this.logger.info('Starting handler scan', { patterns });

    for (const pattern of patterns) {
      const files = await glob(pattern, { absolute: true });
      this.logger.debug(`Found ${files.length} files for pattern: ${pattern}`);

      for (const file of files) {
        try {
          await this.scanFile(file);
        } catch (error) {
          this.logger.error('Failed to scan file', { file, error });
        }
      }
    }

    this.logger.info('Handler scan complete', {
      discovered: this.discoveredHandlers.size,
      patterns,
    });
  }

  /**
   * Scan a single file for handlers
   */
  private async scanFile(filePath: string): Promise<void> {
    try {
      const module = await import(filePath);
      this.registerHandlersFromModule(module, filePath);
    } catch (error) {
      this.logger.error('Failed to import module', { filePath, error });
    }
  }

  /**
   * Register handlers found in a module
   */
  private registerHandlersFromModule(module: any, filePath: string): void {
    for (const [exportName, exported] of Object.entries(module)) {
      if (this.isHandler(exported)) {
        this.registerHandler(exported, exportName, filePath);
      }
    }
  }

  /**
   * Check if an exported value is a handler
   */
  private isHandler(exported: any): boolean {
    if (typeof exported !== 'function') {
      return false;
    }

    // Check for handler metadata added by decorators
    const hasHandlerName = !!(exported as any).__handlerName;
    const hasOperations = Array.isArray((exported as any).__operations);

    return hasHandlerName && hasOperations;
  }

  /**
   * Register a handler with the registry and DI container
   */
  private registerHandler(HandlerClass: any, exportName: string, filePath: string): void {
    const handlerName = HandlerClass.__handlerName;
    const operations = HandlerClass.__operations || [];
    const schedules = HandlerClass.__schedules || [];
    const isDisabled = HandlerClass.__disabled || false;
    const disabledOperations = HandlerClass.__disabledOperations || [];

    if (isDisabled) {
      this.logger.debug('Skipping disabled handler', { handlerName });
      return;
    }

    // Filter out disabled operations
    const enabledOperations = operations.filter((op: any) => {
      const isDisabled = disabledOperations.includes(op.method);
      if (isDisabled) {
        this.logger.debug('Skipping disabled operation', { 
          handlerName, 
          operation: op.name,
          method: op.method 
        });
      }
      return !isDisabled;
    });

    const enabledSchedules = schedules.filter((schedule: any) => {
      const isDisabled = disabledOperations.includes(schedule.operation);
      if (isDisabled) {
        this.logger.debug('Skipping disabled scheduled operation', { 
          handlerName, 
          operation: schedule.operation 
        });
      }
      return !isDisabled;
    });

    // Build metadata
    const metadata: HandlerMetadata = {
      name: handlerName,
      service: this.options.serviceName,
      operations: enabledOperations.map((op: any) => ({
        name: op.name,
        method: op.method,
      })),
      schedules: enabledSchedules.map((schedule: any) => ({
        operation: schedule.operation,
        cronPattern: schedule.cronPattern,
        priority: schedule.priority,
        immediately: schedule.immediately,
        description: schedule.description,
        payload: schedule.payload,
        batch: schedule.batch,
      })),
    };

    // Build configuration with operation handlers
    const operationHandlers: Record<string, any> = {};
    for (const op of enabledOperations) {
      operationHandlers[op.name] = async (payload: any) => {
        const handler = this.container.resolve<IHandler>(handlerName);
        const context: ExecutionContext = {
          type: 'queue',
          metadata: { source: 'queue', timestamp: Date.now() },
        };
        return await handler.execute(op.name, payload, context);
      };
    }

    const configuration: HandlerConfiguration = {
      name: handlerName,
      operations: operationHandlers,
      scheduledJobs: enabledSchedules.map((schedule: any) => {
        const operation = enabledOperations.find((op: any) => op.method === schedule.operation);
        return {
          type: `${handlerName}-${schedule.operation}`,
          operation: operation?.name || schedule.operation,
          cronPattern: schedule.cronPattern,
          priority: schedule.priority || 5,
          immediately: schedule.immediately || false,
          description: schedule.description || `${handlerName} ${schedule.operation}`,
          payload: schedule.payload, // Include payload from decorator
          batch: schedule.batch, // Include batch config from decorator
        };
      }),
    };

    // Register with registry
    this.registry.register(metadata, configuration);

    // Register with DI container if auto-register is enabled
    if (this.options.autoRegister !== false) {
      this.container.register({
        [handlerName]: asFunction(({ serviceContainer }) => {
          return new HandlerClass(serviceContainer);
        }).singleton(),
      });
    }

    // Track discovered handler
    this.discoveredHandlers.set(handlerName, HandlerClass);

    this.logger.info('Registered handler', {
      handlerName,
      exportName,
      filePath,
      operations: enabledOperations.length,
      schedules: enabledSchedules.length,
      disabledOperations: disabledOperations.length,
      service: this.options.serviceName,
    });
  }

  /**
   * Get all discovered handlers
   */
  getDiscoveredHandlers(): Map<string, any> {
    return new Map(this.discoveredHandlers);
  }

  /**
   * Manually register a handler class
   */
  registerHandlerClass(HandlerClass: any, options: { serviceName?: string } = {}): void {
    const serviceName = options.serviceName || this.options.serviceName;
    const originalServiceName = this.options.serviceName;

    // Temporarily override service name if provided
    if (serviceName) {
      this.options.serviceName = serviceName;
    }

    this.registerHandler(HandlerClass, HandlerClass.name, 'manual');

    // Restore original service name
    this.options.serviceName = originalServiceName;
  }
}
