import type { BaseHandler } from '@stock-bot/handlers';
import type { DataIngestionServices } from '../../../types';
import type { EodHandler } from '../eod.handler';
import { EOD_CONFIG } from '../shared';
import { getEodExchangeSuffix } from '../shared/utils';

interface FetchCorporateActionsInput {
  symbol: string;
  exchange: string;
  actionType: 'dividends' | 'splits';
  country?: string;
}

export async function scheduleFetchCorporateActions(
  this: EodHandler
): Promise<{ success: boolean; jobsScheduled: number }> {
  const logger = this.logger;
  
  try {
    logger.info('Scheduling corporate actions fetch jobs');
    
    // Get Canadian exchanges for now
    const canadianExchanges = ['TO', 'V', 'CN', 'NEO'];
    
    // Use OperationTracker to find stale symbols for both dividends and splits
    const allStaleDividends = await this.operationRegistry.getStaleSymbols('eod', 'dividends_update', {
      limit: 50000 // Get all symbols needing update
    });
    
    const allStaleSplits = await this.operationRegistry.getStaleSymbols('eod', 'splits_update', {
      limit: 50000 // Get all symbols needing update
    });
    
    // Filter for Canadian exchanges and non-delisted symbols
    const staleSymbolsDividends = allStaleDividends.filter(item => 
      canadianExchanges.includes(item.symbol.Exchange) && 
      item.symbol.delisted === false
    ); // Remove the slice limit to process all symbols
    
    const staleSymbolsSplits = allStaleSplits.filter(item => 
      canadianExchanges.includes(item.symbol.Exchange) && 
      item.symbol.delisted === false
    ); // Remove the slice limit to process all symbols
    
    if ((!staleSymbolsDividends || staleSymbolsDividends.length === 0) && 
        (!staleSymbolsSplits || staleSymbolsSplits.length === 0)) {
      logger.info('No symbols need corporate actions update');
      return { success: true, jobsScheduled: 0 };
    }
    
    logger.info(`Found ${staleSymbolsDividends.length} symbols needing dividends update and ${staleSymbolsSplits.length} symbols needing splits update`);
    
    let jobsScheduled = 0;
    
    // Schedule dividends jobs
    for (let i = 0; i < staleSymbolsDividends.length; i++) {
      const { symbol } = staleSymbolsDividends[i];
      
      await this.scheduleOperation('fetch-corporate-actions', {
        symbol: symbol.Code,
        exchange: symbol.eodExchange || symbol.Exchange, // Use eodExchange if available
        actionType: 'dividends',
        country: symbol.Country
      }, {
        attempts: 3,
        backoff: {
          type: 'exponential',
          delay: 5000
        },
        delay: i * 200 // Stagger jobs by 200ms per symbol
      });
      jobsScheduled++;
    }
    
    // Schedule splits jobs
    for (let i = 0; i < staleSymbolsSplits.length; i++) {
      const { symbol } = staleSymbolsSplits[i];
      
      await this.scheduleOperation('fetch-corporate-actions', {
        symbol: symbol.Code,
        exchange: symbol.eodExchange || symbol.Exchange, // Use eodExchange if available
        actionType: 'splits',
        country: symbol.Country
      }, {
        attempts: 3,
        backoff: {
          type: 'exponential',
          delay: 5000
        },
        delay: (i * 200) + 100 // Offset splits by 100ms from dividends
      });
      jobsScheduled++;
    }
    
    logger.info(`Successfully scheduled ${jobsScheduled} corporate actions fetch jobs`);
    
    return {
      success: true,
      jobsScheduled
    };
  } catch (error) {
    logger.error('Failed to schedule corporate actions fetch jobs', { error });
    throw error;
  }
}

export async function fetchCorporateActions(
  this: EodHandler,
  input: FetchCorporateActionsInput
): Promise<{ success: boolean; recordsCount: number }> {
  const logger = this.logger;
  const { symbol, exchange, actionType, country } = input;
  
  try {
    logger.info(`Fetching ${actionType} for ${symbol}.${exchange}`);
    
    // Get API key
    const apiKey = EOD_CONFIG.API_TOKEN;
    if (!apiKey) {
      throw new Error('EOD API key not configured');
    }
    
    // Get country if not provided
    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;
    }
    
    // Build URL based on action type
    // Use utility function to handle US symbols and EUFUND special case
    const exchangeSuffix = getEodExchangeSuffix(exchange, symbolCountry);
    
    const endpoint = actionType === 'dividends' ? 'div' : 'splits';
    const url = new URL(`https://eodhd.com/api/${endpoint}/${symbol}.${exchangeSuffix}`);
    url.searchParams.append('api_token', apiKey);
    url.searchParams.append('fmt', 'json');
    
    // Fetch data
    const response = await fetch(url.toString());
    
    if (!response.ok) {
      throw new Error(`EOD ${actionType} API returned ${response.status}: ${response.statusText}`);
    }
    
    const data = await response.json();
    
    // EOD returns an array of corporate actions
    if (!Array.isArray(data)) {
      throw new Error('Invalid response format from EOD API - expected array');
    }
    
    logger.info(`Fetched ${data.length} ${actionType} records for ${symbol}.${exchange}`);
    
    // Log sample records if any
    if (data.length > 0) {
      logger.debug(`Sample ${actionType} for ${symbol}.${exchange}:`, {
        count: data.length,
        samples: data.slice(0, 3).map(record => ({
          date: record.date,
          value: record.value || record.split || record.amount,
          ...record
        }))
      });
    }
    
    if (data.length === 0) {
      // Update symbol to indicate we checked but found no data
      await this.mongodb.collection('eodSymbols').updateOne(
        { Code: symbol, Exchange: exchange },
        { 
          $set: { 
            [`last${actionType.charAt(0).toUpperCase() + actionType.slice(1)}Update`]: new Date(),
            [`has${actionType.charAt(0).toUpperCase() + actionType.slice(1)}`]: false
          } 
        }
      );
      
      return { success: true, recordsCount: 0 };
    }
    
    // Add metadata to each record
    const recordsWithMetadata = data.map(record => ({
      symbol,
      exchange,
      symbolExchange: `${symbol}.${exchange}`,
      ...record,
      actionType,
      updatedAt: new Date(),
      source: 'eod'
    }));
    
    // Determine collection name based on action type
    const collectionName = actionType === 'dividends' ? 'eodDividends' : 'eodSplits';
    
    // Save to MongoDB - use date and symbol as unique identifier
    const result = await this.mongodb.batchUpsert(
      collectionName,
      recordsWithMetadata,
      ['date', 'symbolExchange']
    );
    
    // Update operation tracker based on action type
    const operationName = actionType === 'dividends' ? 'dividends_update' : 'splits_update';
    const eodSearchCode = `${symbol}.${exchange}`;
    await this.operationRegistry.updateOperation('eod', eodSearchCode, operationName, {
      status: 'success',
      recordCount: result.insertedCount,
      metadata: {
        actionType: actionType,
        exchange: exchange,
        hasData: result.insertedCount > 0
      }
    });
    
    logger.info(`Successfully saved ${result.insertedCount} ${actionType} records for ${symbol}.${exchange}`);
    
    return {
      success: true,
      recordsCount: result.insertedCount
    };
  } catch (error) {
    logger.error('Failed to fetch corporate actions', { error, symbol, exchange, actionType });
    
    // Update operation tracker with failure
    const operationName = actionType === 'dividends' ? 'dividends_update' : 'splits_update';
    const eodSearchCode = `${symbol}.${exchange}`;
    await this.operationRegistry.updateOperation('eod', eodSearchCode, operationName, {
      status: 'failure',
      error: error.message
    });
    
    throw error;
  }
}