import * as plugins from '../../plugins.js';
import type { IStockProvider, IProviderConfig } from '../interfaces/provider.js';
import type {
  IStockPrice,
  IStockDataRequest,
  IStockCurrentRequest,
  IStockBatchCurrentRequest
} from '../interfaces/stockprice.js';

export class YahooFinanceProvider implements IStockProvider {
  public name = 'Yahoo Finance';
  public priority = 100;
  public readonly requiresAuth = false;
  public readonly rateLimit = {
    requestsPerMinute: 100, // Conservative estimate
    requestsPerDay: undefined
  };

  private logger = console;
  private baseUrl = 'https://query1.finance.yahoo.com';
  private userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';

  constructor(private config?: IProviderConfig) {}

  /**
   * Unified data fetching method
   */
  public async fetchData(request: IStockDataRequest): Promise<IStockPrice | IStockPrice[]> {
    switch (request.type) {
      case 'current':
        return this.fetchCurrentPrice(request);
      case 'batch':
        return this.fetchBatchCurrentPrices(request);
      case 'historical':
        throw new Error('Yahoo Finance provider does not support historical data. Use Marketstack provider instead.');
      case 'intraday':
        throw new Error('Yahoo Finance provider does not support intraday data yet. Use Marketstack provider instead.');
      default:
        throw new Error(`Unsupported request type: ${(request as any).type}`);
    }
  }

  /**
   * Fetch current price for a single ticker
   */
  private async fetchCurrentPrice(request: IStockCurrentRequest): Promise<IStockPrice> {
    try {
      const url = `${this.baseUrl}/v8/finance/chart/${request.ticker}`;
      const response = await plugins.smartrequest.SmartRequest.create()
        .url(url)
        .headers({
          'User-Agent': this.userAgent
        })
        .timeout(this.config?.timeout || 10000)
        .get();

      const responseData = await response.json() as any;
      
      if (!responseData?.chart?.result?.[0]) {
        throw new Error(`No data found for ticker ${request.ticker}`);
      }

      const data = responseData.chart.result[0];
      const meta = data.meta;
      
      if (!meta.regularMarketPrice) {
        throw new Error(`No price data available for ${request.ticker}`);
      }

      const stockPrice: IStockPrice = {
        ticker: request.ticker.toUpperCase(),
        price: meta.regularMarketPrice,
        currency: meta.currency || 'USD',
        change: meta.regularMarketPrice - meta.previousClose,
        changePercent: ((meta.regularMarketPrice - meta.previousClose) / meta.previousClose) * 100,
        previousClose: meta.previousClose,
        timestamp: new Date(meta.regularMarketTime * 1000),
        provider: this.name,
        marketState: this.determineMarketState(meta),
        exchange: meta.exchange,
        exchangeName: meta.exchangeName,
        dataType: 'live', // Yahoo provides real-time/near real-time data
        fetchedAt: new Date()
      };

      return stockPrice;
    } catch (error) {
      console.error(`Failed to fetch price for ${request.ticker}:`, error);
      throw new Error(`Yahoo Finance: Failed to fetch price for ${request.ticker}: ${plugins.getErrorMessage(error)}`);
    }
  }

  /**
   * Fetch batch current prices
   */
  private async fetchBatchCurrentPrices(request: IStockBatchCurrentRequest): Promise<IStockPrice[]> {
    try {
      const symbols = request.tickers.join(',');
      const url = `${this.baseUrl}/v8/finance/spark?symbols=${symbols}&range=1d&interval=5m`;
      
      const response = await plugins.smartrequest.SmartRequest.create()
        .url(url)
        .headers({
          'User-Agent': this.userAgent
        })
        .timeout(this.config?.timeout || 15000)
        .get();

      const responseData = await response.json() as any;
      const prices: IStockPrice[] = [];
      
      for (const [ticker, data] of Object.entries(responseData)) {
        if (!data || typeof data !== 'object') continue;
        
        const sparkData = data as any;
        if (!sparkData.previousClose || !sparkData.close?.length) {
          console.warn(`Incomplete data for ${ticker}, skipping`);
          continue;
        }

        const currentPrice = sparkData.close[sparkData.close.length - 1];
        const timestamp = sparkData.timestamp?.[sparkData.timestamp.length - 1];
        
        prices.push({
          ticker: ticker.toUpperCase(),
          price: currentPrice,
          currency: sparkData.currency || 'USD',
          change: currentPrice - sparkData.previousClose,
          changePercent: ((currentPrice - sparkData.previousClose) / sparkData.previousClose) * 100,
          previousClose: sparkData.previousClose,
          timestamp: timestamp ? new Date(timestamp * 1000) : new Date(),
          provider: this.name,
          marketState: sparkData.marketState || 'REGULAR',
          exchange: sparkData.exchange,
          exchangeName: sparkData.exchangeName,
          dataType: 'live', // Yahoo provides real-time/near real-time data
          fetchedAt: new Date()
        });
      }

      if (prices.length === 0) {
        throw new Error('No valid price data received from batch request');
      }

      return prices;
    } catch (error) {
      console.error(`Failed to fetch batch prices:`, error);
      throw new Error(`Yahoo Finance: Failed to fetch batch prices: ${plugins.getErrorMessage(error)}`);
    }
  }

  public async isAvailable(): Promise<boolean> {
    try {
      // Test with a well-known ticker
      await this.fetchData({ type: 'current', ticker: 'AAPL' });
      return true;
    } catch (error) {
      console.warn('Yahoo Finance provider is not available:', error);
      return false;
    }
  }

  public supportsMarket(market: string): boolean {
    // Yahoo Finance supports most major markets
    const supportedMarkets = ['US', 'UK', 'DE', 'FR', 'JP', 'CN', 'HK', 'AU', 'CA'];
    return supportedMarkets.includes(market.toUpperCase());
  }

  public supportsTicker(ticker: string): boolean {
    // Basic validation - Yahoo supports most tickers
    return /^[A-Z0-9\.\-]{1,10}$/.test(ticker.toUpperCase());
  }

  private determineMarketState(meta: any): 'PRE' | 'REGULAR' | 'POST' | 'CLOSED' {
    const marketState = meta.marketState?.toUpperCase();
    
    switch (marketState) {
      case 'PRE':
        return 'PRE';
      case 'POST':
        return 'POST';
      case 'REGULAR':
        return 'REGULAR';
      default:
        // Check if market is currently open based on timestamps
        const now = Date.now() / 1000;
        const regularMarketTime = meta.regularMarketTime;
        const timeDiff = now - regularMarketTime;
        
        // If last update was more than 1 hour ago, market is likely closed
        return timeDiff > 3600 ? 'CLOSED' : 'REGULAR';
    }
  }
}
