/**
 * Auto-registration utilities for handlers
 * Automatically discovers and registers handlers based on file patterns
 */

import { readdirSync, statSync } from 'fs';
import { join, relative } from 'path';
import { getLogger } from '@stock-bot/logger';
import { BaseHandler } from '../base/BaseHandler';
import type { IServiceContainer } from '@stock-bot/types';

const logger = getLogger('handler-auto-register');

/**
 * Recursively find all handler files in a directory
 */
function findHandlerFiles(dir: string, pattern = '.handler.'): string[] {
  const files: string[] = [];

  function scan(currentDir: string) {
    const entries = readdirSync(currentDir);

    for (const entry of entries) {
      const fullPath = join(currentDir, entry);
      const stat = statSync(fullPath);

      if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
        scan(fullPath);
      } else if (stat.isFile() && entry.includes(pattern) && entry.endsWith('.ts')) {
        files.push(fullPath);
      }
    }
  }

  scan(dir);
  return files;
}

/**
 * Extract handler classes from a module
 */
function extractHandlerClasses(
  module: any
): Array<new (services: IServiceContainer) => BaseHandler> {
  const handlers: Array<new (services: IServiceContainer) => BaseHandler> = [];

  for (const key of Object.keys(module)) {
    const exported = module[key];

    // Check if it's a class that extends BaseHandler
    if (
      typeof exported === 'function' &&
      exported.prototype &&
      exported.prototype instanceof BaseHandler
    ) {
      handlers.push(exported);
    }
  }

  return handlers;
}

/**
 * Auto-register all handlers in a directory
 * @param directory The directory to scan for handlers
 * @param services The service container to inject into handlers
 * @param options Configuration options
 */
export async function autoRegisterHandlers(
  directory: string,
  services: IServiceContainer,
  options: {
    pattern?: string;
    exclude?: string[];
    dryRun?: boolean;
  } = {}
): Promise<{ registered: string[]; failed: string[] }> {
  const { pattern = '.handler.', exclude = [], dryRun = false } = options;
  const registered: string[] = [];
  const failed: string[] = [];

  try {
    logger.info('Starting auto-registration of handlers', { directory, pattern });

    // Find all handler files
    const handlerFiles = findHandlerFiles(directory, pattern);
    logger.debug(`Found ${handlerFiles.length} handler files`, { files: handlerFiles });

    // Process each handler file
    for (const file of handlerFiles) {
      const relativePath = relative(directory, file);

      // Skip excluded files
      if (exclude.some(ex => relativePath.includes(ex))) {
        logger.debug(`Skipping excluded file: ${relativePath}`);
        continue;
      }

      try {
        // Import the module
        const module = await import(file);
        const handlerClasses = extractHandlerClasses(module);

        if (handlerClasses.length === 0) {
          logger.warn(`No handler classes found in ${relativePath}`);
          continue;
        }

        // Register each handler class
        for (const HandlerClass of handlerClasses) {
          const handlerName = HandlerClass.name;

          // Check if handler is disabled
          if ((HandlerClass as any).__disabled) {
            logger.info(`Skipping disabled handler: ${handlerName} from ${relativePath}`);
            continue;
          }

          if (dryRun) {
            logger.info(`[DRY RUN] Would register handler: ${handlerName} from ${relativePath}`);
            registered.push(handlerName);
          } else {
            logger.info(`Registering handler: ${handlerName} from ${relativePath}`);

            // Create instance and register
            const handler = new HandlerClass(services);
            handler.register();

            registered.push(handlerName);
            logger.info(`Successfully registered handler: ${handlerName}`);
          }
        }
      } catch (error) {
        logger.error(`Failed to process handler file: ${relativePath}`, { error });
        failed.push(relativePath);
      }
    }

    logger.info('Auto-registration complete', {
      totalFiles: handlerFiles.length,
      registered: registered.length,
      failed: failed.length,
    });

    return { registered, failed };
  } catch (error) {
    logger.error('Auto-registration failed', { error });
    throw error;
  }
}

/**
 * Create a handler registry that auto-discovers handlers
 */
export function createAutoHandlerRegistry(services: IServiceContainer) {
  return {
    /**
     * Register all handlers from a directory
     */
    async registerDirectory(
      directory: string,
      options?: Parameters<typeof autoRegisterHandlers>[2]
    ) {
      return autoRegisterHandlers(directory, services, options);
    },

    /**
     * Register handlers from multiple directories
     */
    async registerDirectories(
      directories: string[],
      options?: Parameters<typeof autoRegisterHandlers>[2]
    ) {
      const results = {
        registered: [] as string[],
        failed: [] as string[],
      };

      for (const dir of directories) {
        const result = await autoRegisterHandlers(dir, services, options);
        results.registered.push(...result.registered);
        results.failed.push(...result.failed);
      }

      return results;
    },
  };
}
