import { useEffect, useRef } from 'react'
import { createChart, IChartApi, ISeriesApi } from 'lightweight-charts'

function buildWsUrl(pathAndQuery: string) {
  const api = import.meta.env.VITE_API_URL || 'http://localhost:8000/api'
  const base = api.replace(/\/$/, '') // trim trailing slash
  const wsBase = base.replace(/^http/, 'ws').replace(/\/$/, '')
  return wsBase + pathAndQuery
}

type Bar = { time: any; open: number; high: number; low: number; close: number }

function tfToSeconds(tf: string): number {
  switch (tf) {
    case '1m': return 60
    case '5m': return 5 * 60
    case '15m': return 15 * 60
    case '30m': return 30 * 60
    case '1h': return 60 * 60
    case '4h': return 4 * 60 * 60
    default: return 60
  }
}

export default function LiveKlineChart({ symbol, timeframe = '1m' }: { symbol: string; timeframe?: string }) {
  const containerRef = useRef<HTMLDivElement | null>(null)
  const chartRef = useRef<IChartApi | null>(null)
  const seriesRef = useRef<ISeriesApi<'Candlestick'> | null>(null)
  const wsRef = useRef<WebSocket | null>(null)
  const bucketSecondsRef = useRef<number>(tfToSeconds(timeframe))
  const bucketsRef = useRef<Map<number, Bar>>(new Map())

  useEffect(() => {
    if (!containerRef.current) return
    const chart = createChart(containerRef.current, {
      layout: { background: { color: '#0f172a' }, textColor: '#e2e8f0' },
      grid: { vertLines: { color: '#1f2937' }, horzLines: { color: '#1f2937' } },
      rightPriceScale: { borderColor: '#1f2937' },
      timeScale: { borderColor: '#1f2937', timeVisible: true, secondsVisible: false },
      height: 320,
      width: containerRef.current.clientWidth,
    })
    chartRef.current = chart
    const series = chart.addCandlestickSeries({
      upColor: '#16a34a', downColor: '#dc2626', borderUpColor: '#16a34a', borderDownColor: '#dc2626', wickUpColor: '#16a34a', wickDownColor: '#dc2626',
    })
    seriesRef.current = series

    const onResize = () => {
      if (!containerRef.current || !chartRef.current) return
      chartRef.current.applyOptions({ width: containerRef.current.clientWidth })
    }
    window.addEventListener('resize', onResize)
    return () => {
      window.removeEventListener('resize', onResize)
      chart.remove()
    }
  }, [])

  // Keep a rolling 1m history to allow instant timeframe switches without refetch
  const history1mRef = useRef<Bar[]>([])

  // Load initial 1m history when symbol changes
  useEffect(() => {
    ;(async () => {
      try {
        bucketsRef.current = new Map()
        if (seriesRef.current) seriesRef.current.setData([])
        const { marketDataApi } = await import('@/services/api')
        const rows = await marketDataApi.getOHLCV(symbol, '1m', 1000)
        const bars: Bar[] = rows.map(r => ({ time: r.time as any, open: r.open, high: r.high, low: r.low, close: r.close }))
        history1mRef.current = bars
        // Build current timeframe view from 1m history
        const tfSec = tfToSeconds(timeframe)
        const map = new Map<number, Bar>()
        for (const r of bars) {
          const t = r.time as number
          const b = Math.floor(t / tfSec) * tfSec
          const cur = map.get(b)
          if (!cur) {
            map.set(b, { time: b as any, open: r.open, high: r.high, low: r.low, close: r.close })
          } else {
            cur.high = Math.max(cur.high, r.high)
            cur.low = Math.min(cur.low, r.low)
            cur.close = r.close
          }
        }
        bucketsRef.current = map
        if (seriesRef.current) {
          seriesRef.current.setData(Array.from(map.values()))
        }
      } catch {}
    })()
  }, [symbol])

  // On timeframe change, rebuild view from 1m history only (no refetch)
  useEffect(() => {
    bucketSecondsRef.current = tfToSeconds(timeframe)
    const tfSec = bucketSecondsRef.current
    const map = new Map<number, Bar>()
    for (const r of history1mRef.current) {
      const t = r.time as number
      const b = Math.floor(t / tfSec) * tfSec
      const cur = map.get(b)
      if (!cur) {
        map.set(b, { time: b as any, open: r.open, high: r.high, low: r.low, close: r.close })
      } else {
        cur.high = Math.max(cur.high, r.high)
        cur.low = Math.min(cur.low, r.low)
        cur.close = r.close
      }
    }
    bucketsRef.current = map
    if (seriesRef.current) seriesRef.current.setData(Array.from(map.values()))
  }, [timeframe])

  useEffect(() => {
    // Always subscribe to 1m from backend to minimize upstream loads
    const path = `/stream/klines?symbols=${encodeURIComponent(symbol)}&timeframe=1m`
    const url = buildWsUrl(path)
    const ws = new WebSocket(url)
    wsRef.current = ws
    ws.onmessage = (ev) => {
      try {
        const msg = JSON.parse(ev.data)
        if (!msg || msg.symbol.replace('/', '') !== symbol.replace('/', '')) return
        const t = Math.floor(Date.parse(msg.close_time) / 1000)
        // Update rolling 1m history (append or replace last if same minute)
        const last = history1mRef.current[history1mRef.current.length - 1]
        if (last && (last.time as number) === t) {
          // update last bar
          last.high = Math.max(last.high, msg.high)
          last.low = Math.min(last.low, msg.low)
          last.close = msg.close
        } else if (!last || (last.time as number) < t) {
          history1mRef.current.push({ time: t as any, open: msg.open, high: msg.high, low: msg.low, close: msg.close })
          if (history1mRef.current.length > 2000) history1mRef.current.shift()
        }
        // Update current timeframe bucket
        const bucket = Math.floor(t / bucketSecondsRef.current) * bucketSecondsRef.current
        const existing = bucketsRef.current.get(bucket)
        const update: Bar = existing
          ? {
              time: bucket as any,
              open: existing.open,
              high: Math.max(existing.high, msg.high),
              low: Math.min(existing.low, msg.low),
              close: msg.close,
            }
          : {
              time: bucket as any,
              open: msg.open,
              high: msg.high,
              low: msg.low,
              close: msg.close,
            }
        bucketsRef.current.set(bucket, update)
        seriesRef.current?.update(update)
      } catch {}
    }
    ws.onerror = () => {}
    return () => { try { ws.close() } catch {} }
  }, [symbol])

  return (
    <div className="card">
      <div className="title">{symbol} — {timeframe}</div>
      <div ref={containerRef} className="chart" />
    </div>
  )
}
