"use client";

import { useEffect, useMemo, useRef, useState, type KeyboardEvent, type MouseEvent } from "react";
import { simulationSnapshot } from "@/lib/simulation.generated";

type CurvePoint = { ts: string; equity: number; drawdown?: number };

const money = (value: number, currency: "USD" | "CNY") => `${currency === "CNY" ? "¥" : "$"}${value.toLocaleString("zh-CN", { maximumFractionDigits: 2 })}`;
const time = (value: string, seconds = true) => new Date(value).toLocaleTimeString("zh-CN", {
  hour12: false,
  hour: "2-digit",
  minute: "2-digit",
  second: seconds ? "2-digit" : undefined,
  timeZone: "Asia/Shanghai",
});

export function EquityCanvas({
  compact = false,
  points,
  curve,
  currency = "USD",
  axisLabel,
}: {
  compact?: boolean;
  points?: readonly number[];
  curve?: readonly CurvePoint[];
  currency?: "USD" | "CNY";
  axisLabel?: string;
}) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
  const series = useMemo<CurvePoint[]>(() => {
    if (curve?.length) return curve.map((point) => ({ ...point }));
    if (points?.length) {
      const end = new Date(simulationSnapshot.syncedAt).getTime();
      return points.map((equity, index) => ({
        equity,
        ts: new Date(end - (points.length - 1 - index) * 1_000).toISOString(),
      }));
    }
    return simulationSnapshot.equityCurve.map((point) => ({ ...point }));
  }, [curve, points]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || series.length < 2) return;
    const draw = () => {
      const rect = canvas.getBoundingClientRect();
      const dpr = window.devicePixelRatio || 1;
      canvas.width = Math.max(1, Math.round(rect.width * dpr));
      canvas.height = Math.max(1, Math.round(rect.height * dpr));
      const context = canvas.getContext("2d");
      if (!context) return;
      context.setTransform(dpr, 0, 0, dpr, 0, 0);
      const width = rect.width;
      const height = rect.height;
      const pad = compact ? { left: 14, right: 14, top: 14, bottom: 14 } : { left: 130, right: 30, top: 28, bottom: 56 };
      const plotWidth = Math.max(1, width - pad.left - pad.right);
      const plotHeight = Math.max(1, height - pad.top - pad.bottom);
      const values = series.map((point) => point.equity);
      const rawMin = Math.min(...values);
      const rawMax = Math.max(...values);
      const margin = Math.max((rawMax - rawMin) * 0.08, 1);
      const min = rawMin - margin;
      const max = rawMax + margin;
      const range = max - min || 1;
      const plotted = series.map((point, index) => ({
        x: pad.left + index * plotWidth / (series.length - 1),
        y: pad.top + (max - point.equity) * plotHeight / range,
      }));

      context.clearRect(0, 0, width, height);
      context.strokeStyle = "#1b292d";
      context.fillStyle = "#71827f";
      context.lineWidth = 1;
      context.font = `${compact ? 10 : 15}px var(--font-mono), monospace`;
      context.textBaseline = "middle";
      for (let index = 0; index < 4; index += 1) {
        const y = pad.top + index * plotHeight / 3;
        context.beginPath();
        context.moveTo(pad.left, y);
        context.lineTo(width - pad.right, y);
        context.stroke();
        if (!compact) {
          const value = max - index * range / 3;
          context.textAlign = "right";
          context.fillText(money(value, currency), pad.left - 10, y);
        }
      }

      if (!compact) {
        const labels = [0, Math.floor((series.length - 1) / 2), series.length - 1];
        context.textBaseline = "top";
        labels.forEach((index, position) => {
          context.textAlign = position === 0 ? "left" : position === 2 ? "right" : "center";
          context.fillText(time(series[index].ts), plotted[index].x, height - pad.bottom + 17);
        });
        context.save();
        context.translate(24, pad.top + plotHeight / 2);
        context.rotate(-Math.PI / 2);
        context.textAlign = "center";
        context.fillStyle = "#8fa19d";
        context.fillText(axisLabel ?? `账户权益（${currency}）`, 0, 0);
        context.restore();
      }

      const gradient = context.createLinearGradient(0, pad.top, 0, height - pad.bottom);
      gradient.addColorStop(0, "rgba(74,230,167,.26)");
      gradient.addColorStop(1, "rgba(74,230,167,0)");
      context.beginPath();
      context.moveTo(plotted[0].x, height - pad.bottom);
      plotted.forEach((point) => context.lineTo(point.x, point.y));
      context.lineTo(plotted.at(-1)!.x, height - pad.bottom);
      context.closePath();
      context.fillStyle = gradient;
      context.fill();

      context.beginPath();
      plotted.forEach((point, index) => index ? context.lineTo(point.x, point.y) : context.moveTo(point.x, point.y));
      context.strokeStyle = "#4ae6a7";
      context.lineWidth = 2;
      context.stroke();

      const activeIndex = selectedIndex == null ? series.length - 1 : Math.min(selectedIndex, series.length - 1);
      const active = plotted[activeIndex];
      if (!compact && selectedIndex != null) {
        context.setLineDash([4, 4]);
        context.strokeStyle = "rgba(232,191,100,.65)";
        context.beginPath();
        context.moveTo(active.x, pad.top);
        context.lineTo(active.x, height - pad.bottom);
        context.moveTo(pad.left, active.y);
        context.lineTo(width - pad.right, active.y);
        context.stroke();
        context.setLineDash([]);
      }
      context.fillStyle = selectedIndex == null ? "#4ae6a7" : "#e8bf64";
      context.beginPath();
      context.arc(active.x, active.y, selectedIndex == null ? 4 : 5, 0, Math.PI * 2);
      context.fill();
    };
    draw();
    window.addEventListener("resize", draw);
    return () => window.removeEventListener("resize", draw);
  }, [axisLabel, compact, currency, selectedIndex, series]);

  const selectFromClientX = (clientX: number) => {
    if (compact || series.length < 2 || !canvasRef.current) return;
    const rect = canvasRef.current.getBoundingClientRect();
    const left = 130;
    const right = 30;
    const ratio = Math.min(1, Math.max(0, (clientX - rect.left - left) / Math.max(1, rect.width - left - right)));
    setSelectedIndex(Math.round(ratio * (series.length - 1)));
  };
  const onClick = (event: MouseEvent<HTMLCanvasElement>) => selectFromClientX(event.clientX);
  const onKeyDown = (event: KeyboardEvent<HTMLCanvasElement>) => {
    if (compact) return;
    const current = selectedIndex ?? series.length - 1;
    if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
    event.preventDefault();
    if (event.key === "Home") setSelectedIndex(0);
    else if (event.key === "End") setSelectedIndex(series.length - 1);
    else setSelectedIndex(Math.min(series.length - 1, Math.max(0, current + (event.key === "ArrowRight" ? 1 : -1))));
  };
  const safeSelectedIndex = selectedIndex == null ? null : Math.min(selectedIndex, series.length - 1);
  const selected = safeSelectedIndex == null ? null : series[safeSelectedIndex];
  const tooltipLeft = safeSelectedIndex == null || series.length < 2 ? 0 : 8 + safeSelectedIndex / (series.length - 1) * 84;

  return <div className={`equity-chart-wrap ${compact ? "compact" : "interactive"}`}>
    <canvas
      className={compact ? "equity-canvas compact" : "equity-canvas"}
      ref={canvasRef}
      aria-label={compact ? "模拟账户权益曲线" : "可交互模拟账户权益曲线；点击某个点查看北京时间和账户权益"}
      tabIndex={compact ? -1 : 0}
      onClick={onClick}
      onKeyDown={onKeyDown}
    />
    {!compact && selected && <div className="chart-tooltip" role="status" style={{ left: `${tooltipLeft}%` }}>
      <span>北京时间 {time(selected.ts)}</span>
      <b>账户权益 {money(selected.equity, currency)}</b>
    </div>}
    {!compact && <div className="chart-hint">点击曲线查看时间与权益 · 键盘 ← → 可逐点切换</div>}
  </div>;
}
