import {
  BaseHandler,
  Handler,
  Operation,
  RateLimit,
  ScheduledOperation
} from '@stock-bot/handlers';
import type { OperationRegistry } from '../../shared/operation-manager';
import type { DataIngestionServices } from '../../types';
import {
  crawlIntraday,
  fetchBulkFundamentals,
  fetchCorporateActions,
  fetchExchanges,
  fetchIntraday,
  fetchPrices,
  fetchSingleFundamentals,
  fetchSymbolChangeHistory,
  fetchSymbols,
  scheduleFetchCorporateActions,
  scheduleFetchFundamentals,
  scheduleFetchPrices,
  scheduleFetchSymbols,
  scheduleIntradayCrawl
} from './actions';
import { createEODOperationRegistry } from './shared';

/**
 * EOD (End of Day) Handler demonstrating advanced rate limiting
 * 
 * Handler-level limits apply to all operations unless overridden
 * Operations can specify just a cost to use handler limits, or override with custom limits
 */
@Handler('eod')
@RateLimit({
  limits: [
    { points: 1000, duration: 60 },        // 1000 points per minute
    { points: 100500, duration: 86400 }, // 100,500 points per day
  ],
})
export class EodHandler extends BaseHandler<DataIngestionServices> {
  public operationRegistry: OperationRegistry;

  constructor(services: any) {
    super(services);
    
    // Use pre-initialized registry if available, otherwise create new one
    if ((EodHandler as any).operationRegistry) {
      this.operationRegistry = (EodHandler as any).operationRegistry;
      this.logger.info('Using pre-initialized EOD operation registry');
    } else {
      // Fallback: Initialize operation registry with EOD provider
      createEODOperationRegistry(this.mongodb, this.logger)
        .then(registry => {
          this.operationRegistry = registry;
          this.logger.info('EOD operation registry initialized successfully');
        })
        .catch(error => {
          this.logger.error('Failed to initialize EOD operations', { error });
        });
    }
  }

  /**
   * Fetch exchanges list from EOD
   * Runs weekly on Sundays at midnight
   */
  @Operation('fetch-exchanges')
  @ScheduledOperation('fetch-exchanges', '0 0 * * 0')
  @RateLimit(1) // 1 point per call
  fetchExchanges = fetchExchanges;

  /**
   * Schedule symbol fetching for all exchanges
   * Runs daily at 1 AM
   */
  @Operation('schedule-fetch-symbols')
  @ScheduledOperation('schedule-fetch-symbols', '0 1 * * *')
  @RateLimit(1) // 1 point for scheduling
  scheduleFetchSymbols = scheduleFetchSymbols;

  /**
   * Fetch symbols for a specific exchange
   * Called by schedule-fetch-symbols for each exchange
   */
  @Operation('fetch-symbols')
  @RateLimit(1) // 1 point per exchange
  fetchSymbols = fetchSymbols;

  /**
   * Schedule price fetching for all symbols not updated in last week
   * Runs daily at 2 AM
   */
  @Operation('schedule-fetch-prices')
  @ScheduledOperation('schedule-fetch-prices', '0 2 * * *')
  @RateLimit(1) // 1 point for scheduling
  scheduleFetchPrices = scheduleFetchPrices;

  /**
   * Fetch historical prices for a specific symbol
   * Called by schedule-fetch-prices for each symbol
   */
  @Operation('fetch-prices')
  @RateLimit(1) // 1 point per price fetch
  fetchPrices = fetchPrices;

  /**
   * Schedule intraday crawl for symbols needing intraday data
   * Runs daily at 3 AM
   */
  @Operation('schedule-intraday-crawl')
  @ScheduledOperation('schedule-intraday-crawl', '0 3 * * *', {
    immediately: true,
  })
  @RateLimit(1) // 1 point for scheduling
  scheduleIntradayCrawl = scheduleIntradayCrawl;

  /**
   * Crawl intraday data for a specific symbol and interval
   * Handles resumption and batch processing
   */
  @Operation('crawl-intraday')
  @RateLimit(5) // 5 points per crawl batch
  crawlIntraday = crawlIntraday;

  /**
   * Fetch intraday data for a specific date range
   * Called by crawl-intraday
   */
  @Operation('fetch-intraday')
  @RateLimit(5) // 5 points per intraday fetch
  fetchIntraday = fetchIntraday;

  /**
   * Schedule bulk fundamentals fetch for symbols
   * DISABLED - Not scheduled automatically
   */
  @Operation('schedule-fetch-fundamentals')
  // @ScheduledOperation('schedule-fetch-fundamentals', '0 4 * * 0') // Disabled - would run weekly at 4 AM on Sundays
  @RateLimit(1) // 1 point for scheduling
  scheduleFetchFundamentals = scheduleFetchFundamentals;

  /**
   * Fetch fundamentals for up to 500 symbols in bulk
   * Called by schedule-fetch-fundamentals for non-ETFs
   */
  @Operation('fetch-bulk-fundamentals')
  @RateLimit(600) // 100 base + up to 500 symbols = 600 points max
  fetchBulkFundamentals = fetchBulkFundamentals;

  /**
   * Fetch fundamentals for a single symbol
   * Called by schedule-fetch-fundamentals for ETFs
   */
  @Operation('fetch-single-fundamentals')
  @RateLimit(10) // 10 points per single fundamentals fetch
  fetchSingleFundamentals = fetchSingleFundamentals;

  /**
   * Fetch symbol change history
   * Runs weekly on Mondays at 5 AM
   */
  @Operation('fetch-symbol-change-history')
  @ScheduledOperation('fetch-symbol-change-history', '0 5 * * 1')
  @RateLimit(5) // 5 points per request
  fetchSymbolChangeHistory = fetchSymbolChangeHistory;

  /**
   * Schedule corporate actions (dividends and splits) fetch
   * DISABLED - Corporate actions likely come with fundamentals
   */
  @Operation('schedule-fetch-corporate-actions')
  // @ScheduledOperation('schedule-fetch-corporate-actions', '0 6 1 * *') // Disabled - would run monthly on the 1st at 6 AM
  @RateLimit(1) // 1 point for scheduling
  scheduleFetchCorporateActions = scheduleFetchCorporateActions;

  /**
   * Fetch dividends or splits for a specific symbol
   * Called by schedule-fetch-corporate-actions
   */
  @Operation('fetch-corporate-actions')
  @RateLimit(10) // 10 points per fetch
  fetchCorporateActions = fetchCorporateActions;
}