import type { CandlestickData, MarketData, ChartInterval } from '../types';

const API_BASE = '/api/market-data';

export class ChartDataService {
  static async getHistoricalData(
    symbol: string,
    interval: ChartInterval,
    startDate?: Date,
    endDate?: Date
  ): Promise<CandlestickData[]> {
    const params = new URLSearchParams({
      symbol,
      interval,
      ...(startDate && { start: startDate.toISOString() }),
      ...(endDate && { end: endDate.toISOString() }),
    });

    const response = await fetch(`${API_BASE}/historical?${params}`);
    
    if (!response.ok) {
      throw new Error('Failed to fetch historical data');
    }

    const data = await response.json();
    
    // Transform data to match lightweight-charts format
    const candlestickData = data.map((candle: any) => {
      // Convert time to Unix timestamp in seconds
      let time: number;
      const rawTime = candle.timestamp || candle.time;
      
      if (typeof rawTime === 'string') {
        // If it's a date string, parse it
        time = Math.floor(new Date(rawTime).getTime() / 1000);
      } else if (typeof rawTime === 'number') {
        // If it's already a number, check if it's in milliseconds or seconds
        time = rawTime > 9999999999 ? Math.floor(rawTime / 1000) : rawTime;
      } else {
        // Default to current time if invalid
        time = Math.floor(Date.now() / 1000);
      }
      
      return {
        time,
        open: candle.open,
        high: candle.high,
        low: candle.low,
        close: candle.close,
        volume: candle.volume,
      };
    });

    // Sort by time and remove duplicates
    const sortedData = candlestickData.sort((a, b) => a.time - b.time);
    
    // Remove duplicates by keeping only the last entry for each timestamp
    const uniqueData = sortedData.reduce((acc: CandlestickData[], current) => {
      const lastItem = acc[acc.length - 1];
      
      if (!lastItem || current.time !== lastItem.time) {
        acc.push(current);
      } else {
        // Replace with current if duplicate timestamp
        acc[acc.length - 1] = current;
      }
      
      return acc;
    }, []);

    return uniqueData;
  }

  static async getRealtimeData(symbol: string): Promise<MarketData> {
    const response = await fetch(`${API_BASE}/quote/${symbol}`);
    
    if (!response.ok) {
      throw new Error('Failed to fetch market data');
    }

    return response.json();
  }

  static async searchSymbols(query: string): Promise<Array<{ symbol: string; name: string; exchange: string }>> {
    const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(query)}`);
    
    if (!response.ok) {
      throw new Error('Failed to search symbols');
    }

    return response.json();
  }

  // WebSocket connection for real-time updates
  static subscribeToRealtime(
    symbol: string,
    onUpdate: (data: CandlestickData) => void,
    onError?: (error: Error) => void
  ): () => void {
    const ws = new WebSocket(`ws://localhost:3000/ws/market-data`);
    let isConnected = false;

    ws.onopen = () => {
      isConnected = true;
      // Subscribe to symbol
      ws.send(JSON.stringify({
        type: 'subscribe',
        symbol,
      }));
    };

    ws.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        if (data.symbol === symbol) {
          // Convert timestamp to Unix seconds
          let time: number;
          if (typeof data.timestamp === 'string') {
            time = Math.floor(new Date(data.timestamp).getTime() / 1000);
          } else if (typeof data.timestamp === 'number') {
            time = data.timestamp > 9999999999 ? Math.floor(data.timestamp / 1000) : data.timestamp;
          } else {
            time = Math.floor(Date.now() / 1000);
          }
          
          onUpdate({
            time,
            open: data.open,
            high: data.high,
            low: data.low,
            close: data.close,
            volume: data.volume,
          });
        }
      } catch (error) {
        onError?.(error as Error);
      }
    };

    ws.onerror = (error) => {
      onError?.(new Error('WebSocket error'));
    };

    ws.onclose = () => {
      isConnected = false;
    };

    // Return cleanup function
    return () => {
      if (isConnected) {
        ws.send(JSON.stringify({
          type: 'unsubscribe',
          symbol,
        }));
      }
      ws.close();
    };
  }

  // Convert interval to milliseconds for calculations
  static intervalToMs(interval: ChartInterval): number {
    const map: Record<ChartInterval, number> = {
      '1m': 60 * 1000,
      '5m': 5 * 60 * 1000,
      '15m': 15 * 60 * 1000,
      '30m': 30 * 60 * 1000,
      '1h': 60 * 60 * 1000,
      '4h': 4 * 60 * 60 * 1000,
      '1d': 24 * 60 * 60 * 1000,
      '1w': 7 * 24 * 60 * 60 * 1000,
      '1M': 30 * 24 * 60 * 60 * 1000,
    };
    return map[interval] || map['1d'];
  }

  // Get appropriate date range for interval
  static getDefaultDateRange(interval: ChartInterval): { start: Date; end: Date } {
    const end = new Date();
    const start = new Date();

    switch (interval) {
      case '1m':
      case '5m':
        start.setHours(start.getHours() - 24); // 24 hours
        break;
      case '15m':
      case '30m':
        start.setDate(start.getDate() - 7); // 1 week
        break;
      case '1h':
        start.setDate(start.getDate() - 30); // 1 month
        break;
      case '4h':
        start.setMonth(start.getMonth() - 3); // 3 months
        break;
      case '1d':
        start.setFullYear(start.getFullYear() - 1); // 1 year
        break;
      case '1w':
        start.setFullYear(start.getFullYear() - 3); // 3 years
        break;
      case '1M':
        start.setFullYear(start.getFullYear() - 10); // 10 years
        break;
    }

    return { start, end };
  }
}