import { sleep } from '@stock-bot/di';
import { getLogger } from '@stock-bot/logger';

const logger = getLogger('symbol-search-util');

export interface SearchFunction {
  (query: string): Promise<string[]>;
}

export class SymbolSearchUtil {
  private threshold: number;
  private searchFunction: SearchFunction;
  private maxDepth: number;
  private delay: number;

  constructor(
    searchFunction: SearchFunction,
    threshold: number = 50,
    maxDepth: number = 4,
    delay: number = 100
  ) {
    this.searchFunction = searchFunction;
    this.threshold = threshold;
    this.maxDepth = maxDepth;
    this.delay = delay;
  }

  async searchAllSymbols(): Promise<string[]> {
    logger.info('Starting comprehensive symbol search...');
    const allSymbols: string[] = [];

    // Start with single letters A-Z
    for (let i = 0; i < 26; i++) {
      const singleLetter = String.fromCharCode(65 + i);

      try {
        const symbols = await this.searchRecursive(singleLetter, 1);
        allSymbols.push(...symbols);

        // Add delay between top-level searches
        if (this.delay > 0) {
          await sleep(this.delay);
        }
      } catch (error) {
        logger.error(`Failed to search for "${singleLetter}":`, error);
        // Continue with next letter
      }
    }

    // Remove duplicates
    const uniqueSymbols = [...new Set(allSymbols)];
    logger.info(`Symbol search completed. Found ${uniqueSymbols.length} unique symbols`);
    return uniqueSymbols;
  }

  private async searchRecursive(prefix: string, depth: number): Promise<string[]> {
    try {
      const symbols = await this.searchFunction(prefix);

      logger.debug(`Query "${prefix}" returned ${symbols.length} symbols`);

      // If we're at max depth or results are under threshold, return the symbols
      if (depth >= this.maxDepth || symbols.length < this.threshold) {
        logger.info(`Added ${symbols.length} symbols from query: ${prefix}`);
        return symbols;
      }

      // If we have too many results, go deeper
      logger.info(
        `Query "${prefix}" returned ${symbols.length} results (>= ${this.threshold}), going deeper...`
      );
      const allSymbols: string[] = [];

      for (let i = 0; i < 26; i++) {
        const nextQuery = prefix + String.fromCharCode(65 + i);

        try {
          const deeperSymbols = await this.searchRecursive(nextQuery, depth + 1);
          allSymbols.push(...deeperSymbols);

          // Add delay between recursive calls
          if (this.delay > 0 && depth < 3) {
            // Only delay for first few levels
            await sleep(this.delay);
          }
        } catch (error) {
          logger.error(`Failed recursive search for "${nextQuery}":`, error);
          // Continue with next combination
        }
      }

      return allSymbols;
    } catch (error) {
      logger.error(`Error in recursive search for "${prefix}":`, error);
      return [];
    }
  }

  // Static method for one-off searches
  static async search(
    searchFunction: SearchFunction,
    threshold: number = 50,
    maxDepth: number = 4,
    delay: number = 100
  ): Promise<string[]> {
    const util = new SymbolSearchUtil(searchFunction, threshold, maxDepth, delay);
    return util.searchAllSymbols();
  }
}
