import { useCallback, useEffect, useMemo, useRef } from "react";
import {
  createChart,
  CandlestickSeries,
  HistogramSeries,
  LineSeries,
  ColorType,
  createSeriesMarkers,
  type IChartApi,
  type ISeriesApi,
  type ISeriesMarkersPluginApi,
  type CandlestickData,
  type HistogramData,
  type LineData,
  type Time,
  type UTCTimestamp,
  type SeriesMarker,
  type IPriceLine,
} from "lightweight-charts";
import type { BollingerSeries } from "../lib/technicalIndicators";
import { GMMA_LONG, GMMA_SHORT } from "../lib/patternRules";

export interface OhlcPoint {
  date: string;
  open: number;
  high: number;
  low: number;
  close: number;
  volume?: number;
}

export interface MaConfig {
  period: number;
  color: string;
  label: string;
}

export interface ChartMarker {
  date: string;
  type: "buy" | "sell" | "watch";
  selected?: boolean;
  label?: string;
}

export interface PriceLineConfig {
  price: number;
  label: string;
  color: string;
}

const DEFAULT_MAS: MaConfig[] = [
  { period: 5, color: "#f0c040", label: "MA5" },
  { period: 20, color: "#6fe0d0", label: "MA20" },
  { period: 60, color: "#c97bff", label: "MA60" },
];

const GMMA_SHORT_COLORS = ["#7ec8ff", "#6eb8f5", "#5ea8eb", "#4e98e1", "#3e88d7", "#2e78cd"];
const GMMA_LONG_COLORS = ["#ffb347", "#ffa037", "#ff9027", "#ff8017", "#ff7007", "#e06000"];

function chartPrefs() {
  const w = window.innerWidth;
  const compact = w <= 768;
  return {
    fontSize: compact ? 10 : 11,
    scaleMargins: { top: compact ? 0.12 : 0.1, bottom: compact ? 0.22 : 0.18 },
    priceScaleWidth: compact ? 52 : 60,
  };
}

function toTime(date: string): UTCTimestamp {
  const ms = date.includes("T") ? Date.parse(date) : Date.parse(`${date}T00:00:00Z`);
  return (ms / 1000) as UTCTimestamp;
}

function computeMA(data: OhlcPoint[], period: number): LineData[] {
  const out: LineData[] = [];
  for (let i = period - 1; i < data.length; i++) {
    let sum = 0;
    for (let j = i - period + 1; j <= i; j++) sum += data[j].close;
    out.push({ time: toTime(data[i].date), value: sum / period });
  }
  return out;
}

function markerShape(type: ChartMarker["type"]): SeriesMarker<Time>["shape"] {
  if (type === "buy") return "arrowUp";
  if (type === "sell") return "arrowDown";
  return "circle";
}

function markerColor(type: ChartMarker["type"], selected?: boolean) {
  if (selected) return "#ffe566";
  if (type === "buy") return "#e86a52";
  if (type === "sell") return "#6fcf97";
  return "#f0a040";
}

function markerPosition(type: ChartMarker["type"]): "belowBar" | "aboveBar" {
  return type === "sell" ? "aboveBar" : "belowBar";
}

export default function CandlestickChart({
  data,
  mas = DEFAULT_MAS,
  showVolume = true,
  showGuppy = false,
  showBollinger = false,
  bollinger,
  markers = [],
  priceLines = [],
  focusDate,
  heightClass = "candle-chart",
}: {
  data: OhlcPoint[];
  mas?: MaConfig[];
  showVolume?: boolean;
  showGuppy?: boolean;
  showBollinger?: boolean;
  bollinger?: BollingerSeries | null;
  markers?: ChartMarker[];
  priceLines?: PriceLineConfig[];
  focusDate?: string | null;
  heightClass?: string;
}) {
  const ref = useRef<HTMLDivElement>(null);
  const chartRef = useRef<IChartApi | null>(null);
  const candleRef = useRef<ISeriesApi<"Candlestick"> | null>(null);
  const volRef = useRef<ISeriesApi<"Histogram"> | null>(null);
  const maRefs = useRef<ISeriesApi<"Line">[]>([]);
  const bollRefs = useRef<ISeriesApi<"Line">[]>([]);
  const markerRef = useRef<ISeriesMarkersPluginApi<Time> | null>(null);
  const priceLineRefs = useRef<IPriceLine[]>([]);
  const lastRangeKeyRef = useRef("");

  const bollColors = { upper: "rgba(200,123,255,.55)", mid: "rgba(200,123,255,.25)", lower: "rgba(200,123,255,.55)" };

  const overlayMas = useMemo(() => {
    if (!showGuppy) return mas;
    const short = GMMA_SHORT.map((p, i) => ({
      period: p,
      color: GMMA_SHORT_COLORS[i],
      label: `S${p}`,
    }));
    const long = GMMA_LONG.map((p, i) => ({
      period: p,
      color: GMMA_LONG_COLORS[i],
      label: `L${p}`,
    }));
    return [...short, ...long];
  }, [mas, showGuppy]);

  const clean = useMemo(() => {
    const byDate = new Map<string, OhlcPoint>();
    for (const p of data || []) {
      if (
        p.open == null ||
        p.high == null ||
        p.low == null ||
        p.close == null ||
        Number.isNaN(p.close)
      ) {
        continue;
      }
      byDate.set(p.date, p);
    }
    return [...byDate.values()].sort((a, b) => a.date.localeCompare(b.date));
  }, [data]);

  const applyPrefs = useCallback(() => {
    const chart = chartRef.current;
    if (!chart) return;
    const prefs = chartPrefs();
    chart.applyOptions({
      layout: { fontSize: prefs.fontSize },
      rightPriceScale: { scaleMargins: prefs.scaleMargins, minimumWidth: prefs.priceScaleWidth },
    });
  }, []);

  useEffect(() => {
    if (!ref.current) return;
    const prefs = chartPrefs();
    const chart = createChart(ref.current, {
      layout: {
        background: { type: ColorType.Solid, color: "transparent" },
        textColor: "#e8ecf4",
        fontSize: prefs.fontSize,
      },
      grid: {
        vertLines: { color: "rgba(255,255,255,.06)" },
        horzLines: { color: "rgba(255,255,255,.06)" },
      },
      rightPriceScale: {
        borderColor: "rgba(231,198,107,.25)",
        scaleMargins: prefs.scaleMargins,
        minimumWidth: prefs.priceScaleWidth,
      },
      timeScale: {
        borderColor: "rgba(231,198,107,.25)",
        timeVisible: true,
        secondsVisible: false,
      },
      crosshair: {
        vertLine: { color: "rgba(231,198,107,.35)", width: 1, style: 2 },
        horzLine: { color: "rgba(231,198,107,.35)", width: 1, style: 2 },
      },
      autoSize: true,
      handleScroll: true,
      handleScale: true,
    });

    const candle = chart.addSeries(CandlestickSeries, {
      upColor: "#e86a52",
      downColor: "#6fcf97",
      borderUpColor: "#e86a52",
      borderDownColor: "#6fcf97",
      wickUpColor: "#e86a52",
      wickDownColor: "#6fcf97",
    });

    let vol: ISeriesApi<"Histogram"> | null = null;
    if (showVolume) {
      vol = chart.addSeries(HistogramSeries, {
        color: "rgba(111,224,208,.45)",
        priceFormat: { type: "volume" },
        priceScaleId: "vol",
      });
      chart.priceScale("vol").applyOptions({
        scaleMargins: { top: 0.82, bottom: 0 },
        borderVisible: false,
      });
    }

    const maSeries = overlayMas.map((m) =>
      chart.addSeries(LineSeries, {
        color: m.color,
        lineWidth: 1,
        priceLineVisible: false,
        lastValueVisible: false,
        crosshairMarkerRadius: 0,
      }),
    );

    const bollSeries = showBollinger
      ? (["upper", "mid", "lower"] as const).map((k) =>
          chart.addSeries(LineSeries, {
            color: bollColors[k],
            lineWidth: k === "mid" ? 1 : 1,
            lineStyle: k === "mid" ? 2 : 0,
            priceLineVisible: false,
            lastValueVisible: false,
            crosshairMarkerRadius: 0,
          }),
        )
      : [];

    const markersPlugin = createSeriesMarkers(candle, []);

    chartRef.current = chart;
    candleRef.current = candle;
    volRef.current = vol;
    maRefs.current = maSeries;
    bollRefs.current = bollSeries;
    markerRef.current = markersPlugin;

    const ro = new ResizeObserver(() => applyPrefs());
    ro.observe(ref.current);
    window.addEventListener("resize", applyPrefs);

    return () => {
      ro.disconnect();
      window.removeEventListener("resize", applyPrefs);
      markerRef.current = null;
      chart.remove();
      chartRef.current = null;
      candleRef.current = null;
      volRef.current = null;
      maRefs.current = [];
      bollRefs.current = [];
      priceLineRefs.current = [];
      lastRangeKeyRef.current = "";
    };
  }, [overlayMas, showVolume, showBollinger, applyPrefs]);

  useEffect(() => {
    if (!candleRef.current || !clean.length) return;
    const candles: CandlestickData[] = clean.map((p) => ({
      time: toTime(p.date),
      open: p.open,
      high: p.high,
      low: p.low,
      close: p.close,
    }));
    candleRef.current.setData(candles);

    if (volRef.current) {
      const vols: HistogramData[] = clean
        .filter((p) => p.volume != null && p.volume > 0)
        .map((p) => ({
          time: toTime(p.date),
          value: p.volume!,
          color: p.close >= p.open ? "rgba(232,106,82,.4)" : "rgba(111,207,151,.35)",
        }));
      volRef.current.setData(vols);
    }

    maRefs.current.forEach((series, i) => {
      const cfg = overlayMas[i];
      if (!cfg) return;
      series.setData(computeMA(clean, cfg.period));
    });

    if (showBollinger && bollinger && bollRefs.current.length === 3) {
      const toLine = (pts: { date: string; value: number }[]) =>
        pts.map((p) => ({ time: toTime(p.date), value: p.value }));
      bollRefs.current[0]?.setData(toLine(bollinger.upper));
      bollRefs.current[1]?.setData(toLine(bollinger.mid));
      bollRefs.current[2]?.setData(toLine(bollinger.lower));
    }

    const seriesMarkers: SeriesMarker<Time>[] = markers
      .filter((m) => m.date)
      .map((m) => ({
        time: toTime(m.date),
        position: markerPosition(m.type),
        color: markerColor(m.type, m.selected),
        shape: markerShape(m.type),
        text: m.selected ? `★ ${m.label || ""}` : m.label || "",
        size: m.selected ? 2 : 1,
      }));
    markerRef.current?.setMarkers(seriesMarkers);

    for (const pl of priceLineRefs.current) {
      try {
        candleRef.current.removePriceLine(pl);
      } catch {
        /* ignore */
      }
    }
    priceLineRefs.current = [];
    for (const pl of priceLines) {
      if (pl.price == null || Number.isNaN(pl.price)) continue;
      const line = candleRef.current.createPriceLine({
        price: pl.price,
        title: pl.label,
        color: pl.color,
        lineWidth: 2,
        lineStyle: 2,
        axisLabelVisible: true,
      });
      priceLineRefs.current.push(line);
    }

    const chart = chartRef.current;
    if (chart) {
      const rangeKey = `${clean.length}:${clean[0]?.date}:${clean[clean.length - 1]?.date}`;
      if (focusDate) {
        const t = toTime(focusDate);
        chart.timeScale().setVisibleRange({
          from: (t - 86400 * 40) as UTCTimestamp,
          to: (t + 86400 * 12) as UTCTimestamp,
        });
        lastRangeKeyRef.current = rangeKey;
      } else if (rangeKey !== lastRangeKeyRef.current) {
        // 僅在 K 線資料區間變更時自動 fit，避免拖曳後被 markers 更新打回
        chart.timeScale().fitContent();
        lastRangeKeyRef.current = rangeKey;
      }
    }
  }, [clean, overlayMas, showBollinger, bollinger, markers, priceLines, focusDate]);

  const legendItems = showGuppy
    ? [
        { color: "#4e98e1", label: "顧比短群" },
        { color: "#ff8017", label: "顧比長群" },
      ]
    : mas.map((m) => ({ color: m.color, label: m.label }));

  if (showBollinger) legendItems.push({ color: "#c97bff", label: "布林" });

  return (
    <div className="candle-wrap">
      <div className="candle-legend">
        {legendItems.map((m) => (
          <span key={m.label} className="candle-legend-item">
            <i style={{ background: m.color }} />
            {m.label}
          </span>
        ))}
        {markers.length ? <span className="candle-legend-item">▲買 ▼賣 ●觀察</span> : null}
        <span className="candle-legend-item up">陽線（紅）</span>
        <span className="candle-legend-item down">陰線（綠）</span>
      </div>
      <div ref={ref} className={heightClass} />
    </div>
  );
}