import type { EodHandler } from '../eod.handler';
import { EOD_CONFIG } from '../shared';
import { getEodExchangeSuffix } from '../shared/utils';

interface FetchPricesInput {
  symbol: string;
  exchange: string;
  country?: string; // Optional to maintain backward compatibility
}

export async function scheduleFetchPrices(
  this: EodHandler
): Promise<{ success: boolean; jobsScheduled: number }> {
  const logger = this.logger;
  
  try {
    logger.info('Scheduling price fetch jobs for all symbols');
    
    // Use OperationTracker to find stale symbols
    const staleSymbols = await this.operationRegistry.getStaleSymbols('eod', 'price_update', {
      limit: 50000 // Higher limit to process all symbols
    });
    
    if (!staleSymbols || staleSymbols.length === 0) {
      logger.info('No symbols need price updates');
      return { success: true, jobsScheduled: 0 };
    }
    
    logger.info(`Found ${staleSymbols.length} symbols needing price updates`, {
      symbols: staleSymbols.slice(0, 10).map(s => ({
        symbol: s.symbol.Code,
        exchange: s.symbol.Exchange,
        name: s.symbol.Name,
        lastUpdate: s.lastRun
      }))
    });
    
    let jobsScheduled = 0;
    
    // Schedule jobs with staggered delays
    for (let i = 0; i < staleSymbols.length; i++) {
      const { symbol } = staleSymbols[i];
      logger.debug(`Scheduling price fetch for ${symbol.Code}.${symbol.Exchange}`, {
        name: symbol.Name,
        lastUpdate: staleSymbols[i].lastRun,
        delay: i * 100
      });
      
      await this.scheduleOperation('fetch-prices', {
        symbol: symbol.Code,
        exchange: symbol.eodExchange || symbol.Exchange, // Use eodExchange if available
        country: symbol.Country
      }, {
        attempts: 3,
        backoff: {
          type: 'exponential',
          delay: 5000
        },
        delay: i * 100 // Stagger jobs by 100ms per symbol to avoid rate limit spikes
      });
      jobsScheduled++;
    }
    
    logger.info(`Successfully scheduled ${jobsScheduled} price fetch jobs`);
    
    return {
      success: true,
      jobsScheduled
    };
  } catch (error) {
    logger.error('Failed to schedule price fetch jobs', { error });
    throw error;
  }
}

export async function fetchPrices(
  this: EodHandler,
  input: FetchPricesInput
): Promise<{ success: boolean; priceCount: number }> {
  const logger = this.logger;
  const { symbol, exchange, country } = input;
  
  try {
    logger.info(`Fetching prices for ${symbol}.${exchange}`);
    
    // Use provided country or fetch from database
    let symbolCountry = country;
    if (!symbolCountry) {
      const symbolDoc = await this.mongodb.collection('eodSymbols').findOne({
        Code: symbol,
        Exchange: exchange
      });
      
      if (!symbolDoc) {
        throw new Error(`Symbol ${symbol}.${exchange} not found in database`);
      }
      symbolCountry = symbolDoc.Country;
    }
    
    // Get API key from config
    const apiKey = EOD_CONFIG.API_TOKEN;
    if (!apiKey) {
      throw new Error('EOD API key not configured');
    }
    
    // Build URL for EOD price data
    // Use utility function to handle US symbols and EUFUND special case
    const exchangeSuffix = getEodExchangeSuffix(exchange, symbolCountry);
    
    const url = new URL(`https://eodhd.com/api/eod/${symbol}.${exchangeSuffix}`);
    url.searchParams.append('api_token', apiKey);
    url.searchParams.append('fmt', 'json');
    // Fetch price data from EOD API
    const response = await fetch(url.toString());
    
    if (!response.ok) {
      throw new Error(`EOD Prices API returned ${response.status}: ${response.statusText}`);
    }
    
    const priceData = await response.json();
    
    // EOD returns an array of historical prices
    if (!Array.isArray(priceData)) {
      throw new Error('Invalid response format from EOD API - expected array');
    }
    
    logger.info(`Fetched ${priceData.length} price records for ${symbol}.${exchange}`);
    
    // Log date range of prices
    if (priceData.length > 0) {
      logger.debug(`Price data range for ${symbol}.${exchange}:`, {
        oldest: priceData[0].date,
        newest: priceData[priceData.length - 1].date,
        count: priceData.length
      });
    }
    
    // Add metadata to each price record
    const pricesWithMetadata = priceData.map(price => ({
      symbol,
      exchange,
      symbolExchange: `${symbol}.${exchange}`,
      date: price.date,
      open: price.open,
      high: price.high,
      low: price.low,
      close: price.close,
      adjustedClose: price.adjusted_close,
      volume: price.volume,
    }));
    
    // Save to MongoDB - use date and symbol as unique identifier
    const result = await this.mongodb.batchUpsert(
      'eodPrices',
      pricesWithMetadata,
      ['date', 'symbolExchange']
    );
    
    // Update operation tracker instead of directly updating the symbol
    const eodSearchCode = `${symbol}.${exchange}`;
    await this.operationRegistry.updateOperation('eod', eodSearchCode, 'price_update', {
      status: 'success',
      lastRecordDate: priceData.length > 0 ? priceData[priceData.length - 1].date : null,
      recordCount: priceData.length,
      metadata: {
        insertedCount: result.insertedCount,
        updatedCount: priceData.length - result.insertedCount
      }
    });
    
    logger.info(`Successfully saved ${result.insertedCount} price records for ${symbol}.${exchange}`);
    
    return {
      success: true,
      priceCount: result.insertedCount
    };
  } catch (error: unknown) {
    logger.error('Failed to fetch or save prices', { error, symbol, exchange });
    
    // Update operation tracker with failure
    const eodSearchCode = `${symbol}.${exchange}`;
    await this.operationRegistry.updateOperation('eod', eodSearchCode, 'price_update', {
      status: 'failure',
      error: error.message
    });
    
    throw error;
  }
}