"use client";

import { useEffect, useMemo, useRef, useState } from "react";

import {
  CandlestickSeries,
  ColorType,
  CrosshairMode,
  LineStyle,
  createChart,
  type CandlestickData,
  type IChartApi,
  type ISeriesApi,
  type MouseEventParams,
  type Time,
  type UTCTimestamp,
} from "lightweight-charts";

import { formatMetricValue, formatTimestamp } from "@/lib/format";
import type { PriceCandlePoint } from "@/lib/api/types";

type CandleDatum = {
  ts: string;
  time: UTCTimestamp;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number | null;
  tradeCount: number | null;
};

type HoverState = CandleDatum | null;

function asUtcTimestamp(value: string) {
  return Math.floor(new Date(value).getTime() / 1000) as UTCTimestamp;
}

function formatAxisValue(value: number) {
  return formatMetricValue(value, "close");
}

function spacingFor(width: number, count: number, timeframe: string) {
  if (count <= 0) {
    return 6;
  }
  const multiplier = timeframe === "1m" ? 0.95 : timeframe === "5m" ? 1.05 : 1.2;
  return Math.max(3, Math.min(14, (width / count) * multiplier));
}

export function CandlestickChart({
  points,
  height = 460,
  timeframe = "15m",
}: {
  points: PriceCandlePoint[];
  height?: number;
  timeframe?: string;
}) {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const chartRef = useRef<IChartApi | null>(null);
  const seriesRef = useRef<ISeriesApi<"Candlestick"> | null>(null);
  const resizeObserverRef = useRef<ResizeObserver | null>(null);
  const candleMapRef = useRef<Map<number, CandleDatum>>(new Map());

  const candles = useMemo<CandleDatum[]>(
    () =>
      points.flatMap((point) => {
        if (
          point.open == null ||
          point.high == null ||
          point.low == null ||
          point.close == null
        ) {
          return [];
        }

        return [
          {
            ts: point.ts,
            time: asUtcTimestamp(point.ts),
            open: point.open,
            high: point.high,
            low: point.low,
            close: point.close,
            volume: point.volume,
            tradeCount: point.trade_count,
          },
        ];
      }),
    [points],
  );
  const [hovered, setHovered] = useState<HoverState>(candles.at(-1) ?? null);

  useEffect(() => {
    setHovered(candles.at(-1) ?? null);
  }, [candles]);

  useEffect(() => {
    if (!containerRef.current || candles.length === 0) {
      return;
    }

    const container = containerRef.current;
    const initialWidth = Math.max(container.clientWidth, 320);
    const barSpacing = spacingFor(initialWidth, candles.length, timeframe);
    const chart = createChart(container, {
      autoSize: true,
      height,
      layout: {
        background: { type: ColorType.Solid, color: "#071224" },
        textColor: "#6b7d96",
        fontFamily: "var(--font-mono)",
        attributionLogo: false,
      },
      grid: {
        vertLines: { color: "rgba(120, 133, 168, 0.10)", style: LineStyle.Dashed },
        horzLines: { color: "rgba(120, 133, 168, 0.10)", style: LineStyle.Dashed },
      },
      rightPriceScale: {
        borderColor: "rgba(159, 176, 204, 0.10)",
        scaleMargins: { top: 0.14, bottom: 0.08 },
      },
      timeScale: {
        borderColor: "rgba(159, 176, 204, 0.10)",
        timeVisible: true,
        secondsVisible: false,
        rightOffset: timeframe === "1m" ? 3 : 5,
        barSpacing,
        minBarSpacing: timeframe === "1m" ? 2.5 : timeframe === "5m" ? 3.5 : 4.5,
      },
      crosshair: {
        mode: CrosshairMode.Normal,
        vertLine: {
          color: "rgba(106, 194, 255, 0.28)",
          labelBackgroundColor: "#10203b",
          style: LineStyle.Dashed,
        },
        horzLine: {
          color: "rgba(106, 194, 255, 0.28)",
          labelBackgroundColor: "#10203b",
          style: LineStyle.Dashed,
        },
      },
      localization: {
        priceFormatter: formatAxisValue,
      },
      handleScroll: {
        mouseWheel: true,
        pressedMouseMove: true,
        horzTouchDrag: true,
        vertTouchDrag: false,
      },
      handleScale: {
        axisPressedMouseMove: true,
        mouseWheel: true,
        pinch: true,
      },
    });
    const series = chart.addSeries(CandlestickSeries, {
      upColor: "#9fe6da",
      borderUpColor: "#b7fff2",
      wickUpColor: "#b7fff2",
      downColor: "#ff8f4c",
      borderDownColor: "#ffb07f",
      wickDownColor: "#ffb07f",
      borderVisible: true,
      lastValueVisible: true,
      priceLineVisible: false,
    });

    const seriesData: CandlestickData<Time>[] = candles.map((point) => ({
      time: point.time,
      open: point.open,
      high: point.high,
      low: point.low,
      close: point.close,
    }));
    series.setData(seriesData);
    chart.timeScale().fitContent();

    chartRef.current = chart;
    seriesRef.current = series;
    candleMapRef.current = new Map(candles.map((point) => [Number(point.time), point]));

    const handleCrosshairMove = (param: MouseEventParams<Time>) => {
      if (!seriesRef.current) {
        setHovered(candles.at(-1) ?? null);
        return;
      }
      const seriesDataPoint = param.seriesData.get(seriesRef.current);
      const time = seriesDataPoint?.time;
      if (time == null) {
        setHovered(candles.at(-1) ?? null);
        return;
      }
      setHovered(candleMapRef.current.get(Number(time)) ?? candles.at(-1) ?? null);
    };

    chart.subscribeCrosshairMove(handleCrosshairMove);

    resizeObserverRef.current = new ResizeObserver((entries) => {
      const width = entries[0]?.contentRect.width;
      if (width && chartRef.current) {
        chartRef.current.applyOptions({
          width,
          height,
          timeScale: {
            barSpacing: spacingFor(width, candles.length, timeframe),
          },
        });
      }
    });
    resizeObserverRef.current.observe(container);

    return () => {
      resizeObserverRef.current?.disconnect();
      resizeObserverRef.current = null;
      chart.unsubscribeCrosshairMove(handleCrosshairMove);
      chart.remove();
      chartRef.current = null;
      seriesRef.current = null;
      candleMapRef.current.clear();
    };
  }, [candles, height, timeframe]);

  if (candles.length === 0) {
    return (
      <div className="chartEmpty">
        <p className="eyebrow">No price candles available for the selected range.</p>
      </div>
    );
  }

  const active = hovered ?? candles.at(-1) ?? null;
  const move =
    active && active.open !== 0
      ? ((active.close - active.open) / active.open) * 100
      : null;

  return (
    <div
      data-testid="candlestick-surface"
      style={{ position: "relative", width: "100%", height }}
    >
      <div
        data-testid="candlestick-hover"
        style={{
          position: "absolute",
          top: 12,
          left: 12,
          zIndex: 2,
          pointerEvents: "none",
          display: "grid",
          gap: 5,
          padding: "8px 10px",
          border: "1px solid rgba(159, 176, 204, 0.12)",
          borderRadius: 4,
          background: "rgba(7, 18, 36, 0.76)",
          backdropFilter: "blur(8px)",
          minWidth: 220,
          boxShadow: "0 8px 18px rgba(0, 0, 0, 0.22)",
        }}
      >
        <div className="eyebrow" style={{ fontSize: 9 }}>
          hovered candle
        </div>
        <div style={{ fontSize: 10, color: "#6b7d96", textTransform: "uppercase", letterSpacing: "0.16em" }}>
          {active ? formatTimestamp(active.ts) : "Waiting for crosshair"}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", gap: 8 }}>
          {[
            ["Open", active?.open],
            ["High", active?.high],
            ["Low", active?.low],
            ["Close", active?.close],
            ["Move", move],
            ["Volume", active?.volume],
          ].map(([label, value]) => (
            <div key={label}>
              <div className="eyebrow" style={{ fontSize: 9 }}>{label}</div>
              <div
                style={{
                  marginTop: 3,
                  color: label === "Move" && typeof value === "number" ? (value >= 0 ? "#9fe6da" : "#ff9b67") : "#f5f7fb",
                  fontWeight: 600,
                  fontSize: 12,
                }}
              >
                {typeof value === "number" ? formatMetricValue(value, String(label).toLowerCase()) : "—"}
              </div>
            </div>
          ))}
        </div>
      </div>
      <div ref={containerRef} style={{ width: "100%", height: "100%" }} />
    </div>
  );
}
