import * as LightweightCharts from 'lightweight-charts';
import { useEffect, useRef, useState } from 'react';
import type { CandlestickData, ChartConfig, ChartOptions } from '../types';
import { calculateBollingerBands, calculateEMA, calculateSMA, calculateVWAP } from '../utils/indicators';
import { ChartOverlay } from './ChartOverlay';

interface SymbolChartProps {
  symbol: string;
  data: CandlestickData[];
  config?: Partial<ChartConfig>;
  options?: ChartOptions;
  className?: string;
  onIndicatorSettings?: (id: string) => void;
  onIndicatorRemove?: (id: string) => void;
  onIndicatorToggle?: (id: string) => void;
  onAddIndicator?: () => void;
  activeIndicators?: Array<{
    id: string;
    name: string;
    color: string;
    visible: boolean;
  }>;
}

export function SymbolChart({
  symbol,
  data,
  config = {},
  options = {},
  className = '',
  onIndicatorSettings,
  onIndicatorRemove,
  onIndicatorToggle,
  onAddIndicator,
  activeIndicators = [],
}: SymbolChartProps) {
  const chartContainerRef = useRef<HTMLDivElement>(null);
  const chartRef = useRef<LightweightCharts.IChartApi | null>(null);
  const seriesRef = useRef<LightweightCharts.ISeriesApi<any> | null>(null);
  const volumeSeriesRef = useRef<LightweightCharts.ISeriesApi<any> | null>(null);
  const indicatorSeriesRef = useRef<Map<string, LightweightCharts.ISeriesApi<any>>>(new Map());
  const [chartInitialized, setChartInitialized] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [currentPrice, setCurrentPrice] = useState<{ price: number; change: number; changePercent: number } | null>(null);
  const [indicatorValues, setIndicatorValues] = useState<Map<string, number>>(new Map());

  // Default configuration
  const chartConfig: ChartConfig = {
    symbol,
    interval: '1d',
    chartType: 'candlestick',
    showVolume: true,
    indicators: [],
    theme: 'dark',
    ...config,
  };

  // Set initial price from last candle
  useEffect(() => {
    if (data.length > 0 && !currentPrice) {
      const latest = data[data.length - 1];
      const previous = data.length > 1 ? data[data.length - 2] : latest;
      const change = latest.close - previous.close;
      const changePercent = (change / previous.close) * 100;
      setCurrentPrice({ price: latest.close, change, changePercent });
    }
  }, [data, currentPrice]);

  useEffect(() => {
    if (!chartContainerRef.current) return;

    // Create chart
    const chart = LightweightCharts.createChart(chartContainerRef.current, {
      width: chartContainerRef.current.clientWidth,
      height: chartContainerRef.current.clientHeight || 500,
      layout: {
        background: { type: LightweightCharts.ColorType.Solid, color: chartConfig.theme === 'dark' ? '#0f0f0f' : '#ffffff' },
        textColor: chartConfig.theme === 'dark' ? '#d1d5db' : '#374151',
      },
      grid: {
        vertLines: {
          color: chartConfig.theme === 'dark' ? '#1f2937' : '#e5e7eb',
          visible: true,
        },
        horzLines: {
          color: chartConfig.theme === 'dark' ? '#1f2937' : '#e5e7eb',
          visible: true,
        },
      },
      crosshair: {
        mode: LightweightCharts.CrosshairMode.Normal,
      },
      rightPriceScale: {
        borderColor: chartConfig.theme === 'dark' ? '#1f2937' : '#e5e7eb',
      },
      timeScale: {
        borderColor: chartConfig.theme === 'dark' ? '#1f2937' : '#e5e7eb',
        timeVisible: true,
        secondsVisible: false,
      },
    });

    chartRef.current = chart;
    
    // Create a new map for this chart instance
    indicatorSeriesRef.current = new Map();

    // Create main series based on chart type
    if (chartConfig.chartType === 'candlestick') {
      seriesRef.current = chart.addCandlestickSeries({
        upColor: '#10b981',
        downColor: '#ef4444',
        borderUpColor: '#10b981',
        borderDownColor: '#ef4444',
        wickUpColor: '#10b981',
        wickDownColor: '#ef4444',
      });
    } else if (chartConfig.chartType === 'line') {
      seriesRef.current = chart.addLineSeries({
        color: '#3b82f6',
        lineWidth: 2,
      });
    } else if (chartConfig.chartType === 'area') {
      seriesRef.current = chart.addAreaSeries({
        lineColor: '#3b82f6',
        topColor: '#3b82f6',
        bottomColor: 'rgba(59, 130, 246, 0.1)',
        lineWidth: 2,
      });
    } else if (chartConfig.chartType === 'bar') {
      seriesRef.current = chart.addBarSeries({
        upColor: '#10b981',
        downColor: '#ef4444',
      });
    }

    // Create volume series if data has volume
    if (data.some(d => d.volume)) {
      const volumeIndicator = activeIndicators.find(i => i.id === 'VOLUME');
      const volumeVisible = volumeIndicator ? volumeIndicator.visible : chartConfig.showVolume;
      
      volumeSeriesRef.current = chart.addHistogramSeries({
        color: '#3b82f680',
        priceFormat: {
          type: 'volume',
        },
        priceScaleId: 'volume',
        visible: volumeVisible,
      });
      volumeSeriesRef.current.priceScale().applyOptions({
        scaleMargins: {
            top: 0.9, // highest point of the series will be 70% away from the top
            bottom: 0,
        },
    });
    }

    // Set data
    if (seriesRef.current && data.length > 0) {
      if (chartConfig.chartType === 'candlestick' || chartConfig.chartType === 'bar') {
        seriesRef.current.setData(data);
      } else if (chartConfig.chartType === 'line' || chartConfig.chartType === 'area') {
        // Convert candlestick data to line/area data (using close price)
        const lineData = data.map(d => ({ time: d.time, value: d.close }));
        seriesRef.current.setData(lineData);
      }

      // Set volume data if available
      if (volumeSeriesRef.current) {
        const volumeData = data
          .filter(d => d.volume)
          .map(d => ({
            time: d.time,
            value: d.volume!,
            color: d.close >= d.open ? '#10b98180' : '#ef444480',
          }));
        volumeSeriesRef.current.setData(volumeData);
      }

      // Add indicators - include all indicators from activeIndicators, not just visible ones
      if (activeIndicators && activeIndicators.length > 0) {
        activeIndicators.filter(ind => ind.id !== 'VOLUME').forEach(indicator => {
          const indicatorId = indicator.id;
          if (indicatorId === 'SMA20') {
            const sma20 = calculateSMA(data, 20);
            const sma20Series = chart.addLineSeries({
              color: indicator.style?.color || '#ff9800',
              lineWidth: indicator.style?.lineWidth || 2,
              title: 'SMA 20',
              visible: indicator.visible,
            });
            sma20Series.setData(sma20);
            indicatorSeriesRef.current.set('SMA20', sma20Series);
          }
          
          if (indicatorId === 'SMA50') {
            const sma50 = calculateSMA(data, 50);
            const sma50Series = chart.addLineSeries({
              color: indicator.style?.color || '#2196f3',
              lineWidth: indicator.style?.lineWidth || 2,
              title: 'SMA 50',
              visible: indicator.visible,
            });
            sma50Series.setData(sma50);
            indicatorSeriesRef.current.set('SMA50', sma50Series);
          }
          
          if (indicatorId === 'EMA20') {
            const ema20 = calculateEMA(data, 20);
            const ema20Series = chart.addLineSeries({
              color: indicator.style?.color || '#4caf50',
              lineWidth: indicator.style?.lineWidth || 2,
              title: 'EMA 20',
              visible: indicator.visible,
            });
            ema20Series.setData(ema20);
            indicatorSeriesRef.current.set('EMA20', ema20Series);
          }
          
          if (indicatorId === 'BB') {
            const bb = calculateBollingerBands(data, 20, 2);
            
            const bbUpperSeries = chart.addLineSeries({
              color: indicator.style?.color || '#ff5252',
              lineWidth: 1,
              lineStyle: LightweightCharts.LineStyle.Dashed,
              title: 'BB Upper',
              visible: indicator.visible,
            });
            bbUpperSeries.setData(bb.upper);
            indicatorSeriesRef.current.set('BB_upper', bbUpperSeries);
            
            const bbMiddleSeries = chart.addLineSeries({
              color: '#9e9e9e',
              lineWidth: 1,
              title: 'BB Middle',
              visible: indicator.visible,
            });
            bbMiddleSeries.setData(bb.middle);
            indicatorSeriesRef.current.set('BB_middle', bbMiddleSeries);
            
            const bbLowerSeries = chart.addLineSeries({
              color: '#4caf50',
              lineWidth: 1,
              lineStyle: LightweightCharts.LineStyle.Dashed,
              title: 'BB Lower',
              visible: indicator.visible,
            });
            bbLowerSeries.setData(bb.lower);
            indicatorSeriesRef.current.set('BB_lower', bbLowerSeries);
          }
          
          if (indicatorId === 'VWAP') {
            const vwap = calculateVWAP(data);
            const vwapSeries = chart.addLineSeries({
              color: indicator.style?.color || '#9c27b0',
              lineWidth: indicator.style?.lineWidth || 2,
              title: 'VWAP',
              visible: indicator.visible,
            });
            vwapSeries.setData(vwap);
            indicatorSeriesRef.current.set('VWAP', vwapSeries);
          }
        });
      }

      // Subscribe to crosshair move for tracking values
      chart.subscribeCrosshairMove((param) => {
        if (!param || !param.time || !param.seriesData) return;

        // Update price info
        const mainSeriesData = param.seriesData.get(seriesRef.current!);
        if (mainSeriesData) {
          const currentData = mainSeriesData as any;
          const price = currentData.close || currentData.value || 0;
          
          // Find previous close
          const currentIndex = data.findIndex(d => d.time === param.time);
          const previousClose = currentIndex > 0 ? data[currentIndex - 1].close : price;
          const change = price - previousClose;
          const changePercent = (change / previousClose) * 100;
          
          setCurrentPrice({ price, change, changePercent });
        }

        // Update indicator values
        const newIndicatorValues = new Map<string, number>();
        indicatorSeriesRef.current.forEach((series, indicatorId) => {
          const indicatorData = param.seriesData.get(series);
          if (indicatorData && 'value' in indicatorData) {
            newIndicatorValues.set(indicatorId, indicatorData.value);
          }
        });
        setIndicatorValues(newIndicatorValues);
      });

      // Fit content only on initial load
      chart.timeScale().fitContent();
      setIsLoading(false);
      setChartInitialized(true);
    }

    // Handle resize
    const handleResize = () => {
      if (chartContainerRef.current && chart) {
        chart.applyOptions({
          width: chartContainerRef.current.clientWidth,
          height: chartContainerRef.current.clientHeight,
        });
      }
    };

    window.addEventListener('resize', handleResize);

    // Cleanup
    return () => {
      window.removeEventListener('resize', handleResize);
      setChartInitialized(false);
      if (chart) {
        chart.remove();
      }
    };
  }, [data.length, chartConfig.theme, chartConfig.chartType, activeIndicators.length]); // Include activeIndicators.length to recreate when indicators are added/removed

  // Update data when it changes
  useEffect(() => {
    if (!seriesRef.current || !data.length) return;

    if (chartConfig.chartType === 'candlestick' || chartConfig.chartType === 'bar') {
      seriesRef.current.setData(data);
    } else if (chartConfig.chartType === 'line' || chartConfig.chartType === 'area') {
      const lineData = data.map(d => ({ time: d.time, value: d.close }));
      seriesRef.current.setData(lineData);
    }

    if (volumeSeriesRef.current) {
      const volumeData = data
        .filter(d => d.volume)
        .map(d => ({
          time: d.time,
          value: d.volume!,
          color: d.close >= d.open ? '#10b98180' : '#ef444480',
        }));
      volumeSeriesRef.current.setData(volumeData);
    }

    // Don't fit content on data updates to preserve user's zoom/pan
    // chartRef.current?.timeScale().fitContent();
  }, [data, chartConfig.chartType]);

  // Create a dependency string that captures visibility changes
  const visibilityKey = activeIndicators.map(i => `${i.id}:${i.visible}`).join(',');

  // Handle indicator visibility changes without recreating chart
  useEffect(() => {
    if (!chartRef.current || !chartInitialized) return;

    // Update volume visibility
    if (volumeSeriesRef.current) {
      const volumeIndicator = activeIndicators.find(i => i.id === 'VOLUME');
      const volumeVisible = volumeIndicator ? volumeIndicator.visible : chartConfig.showVolume;
      volumeSeriesRef.current.applyOptions({
        visible: volumeVisible,
      });
    }

    // Update indicator visibility
    activeIndicators.forEach(indicator => {
      if (indicator.id === 'VOLUME') return; // Skip volume, handled above
      
      // For indicators with multiple series (like Bollinger Bands)
      if (indicator.id === 'BB') {
        ['BB_upper', 'BB_middle', 'BB_lower'].forEach(seriesId => {
          const series = indicatorSeriesRef.current.get(seriesId);
          if (series) {
            series.applyOptions({ visible: indicator.visible });
          }
        });
      } else {
        // For single series indicators
        const series = indicatorSeriesRef.current.get(indicator.id);
        if (series) {
          series.applyOptions({ visible: indicator.visible });
        }
      }
    });
  }, [chartConfig.showVolume, visibilityKey, chartInitialized]);

  // Prepare indicator info for overlay - include all indicators
  const indicatorInfo = activeIndicators.map(indicator => ({
    id: indicator.id,
    name: indicator.name,
    value: indicator.visible ? indicatorValues.get(indicator.id) : undefined,
    color: indicator.color,
    visible: indicator.visible,
  }));

  return (
    <div className={`relative ${className}`}>
      {isLoading && (
        <div className="absolute inset-0 flex items-center justify-center bg-background/50 z-10">
          <div className="animate-spin rounded-full h-8 w-8 border-t-2 border-primary-500"></div>
        </div>
      )}
      
      {/* Chart container */}
      <div ref={chartContainerRef} style={{ width: '100%', height: '100%' }} />
      
      {/* Chart Overlay with indicators */}
      {!isLoading && onIndicatorSettings && onIndicatorRemove && (
        <ChartOverlay
          symbol={symbol}
          price={currentPrice?.price}
          change={currentPrice?.change}
          changePercent={currentPrice?.changePercent}
          indicators={indicatorInfo}
          onIndicatorSettings={onIndicatorSettings}
          onIndicatorRemove={onIndicatorRemove}
          onIndicatorToggle={onIndicatorToggle}
          onAddIndicator={onAddIndicator}
        />
      )}
    </div>
  );
}