import { useState, useRef, useEffect, type PointerEvent } from "react";
import { RotateCcw } from "../../icons/SystemIcons";
import {
  clampStudioCustomEasePoints,
  controlPointsForGsapEase,
  serializeStudioCustomEaseData,
  type StudioCustomEaseControlPoints,
} from "./studioMotion";
import { LABEL } from "./MotionPanelFields";

function formatNumericValue(value: number): string {
  const rounded = Math.round(value * 100) / 100;
  return Number.isInteger(rounded)
    ? `${rounded}`
    : rounded.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
}

function cubicBezierPoint(t: number, p1: StudioCustomEaseControlPoints): { x: number; y: number } {
  const inv = 1 - t;
  const inv2 = inv * inv;
  const t2 = t * t;
  return {
    x: 3 * inv2 * t * p1.x1 + 3 * inv * t2 * p1.x2 + t2 * t,
    y: 3 * inv2 * t * p1.y1 + 3 * inv * t2 * p1.y2 + t2 * t,
  };
}

function buildCurvePath(
  points: StudioCustomEaseControlPoints,
  map: (point: { x: number; y: number }) => { x: number; y: number },
): string {
  const commands: string[] = [];
  for (let index = 0; index <= 48; index += 1) {
    const point = map(cubicBezierPoint(index / 48, points));
    commands.push(`${index === 0 ? "M" : "L"}${point.x.toFixed(2)},${point.y.toFixed(2)}`);
  }
  return commands.join(" ");
}

export function EaseCurveEditor({
  points,
  onCommit,
}: {
  points: StudioCustomEaseControlPoints;
  onCommit: (points: StudioCustomEaseControlPoints) => void;
}) {
  const svgRef = useRef<SVGSVGElement | null>(null);
  const [draft, setDraft] = useState(points);
  const draggingRef = useRef<"p1" | "p2" | null>(null);

  useEffect(() => {
    setDraft(points);
  }, [points]);

  const width = 324;
  const height = 214;
  const plot = { left: 46, top: 24, width: 242, height: 146 };
  const yMin = -0.4;
  const yMax = 1.4;

  const mapPoint = (point: { x: number; y: number }) => ({
    x: plot.left + point.x * plot.width,
    y: plot.top + ((yMax - point.y) / (yMax - yMin)) * plot.height,
  });

  const unmapPointer = (event: PointerEvent<SVGSVGElement>) => {
    const rect = svgRef.current?.getBoundingClientRect();
    if (!rect) return null;
    const x = ((event.clientX - rect.left) / rect.width) * width;
    const y = ((event.clientY - rect.top) / rect.height) * height;
    return clampStudioCustomEasePoints({
      x1: draggingRef.current === "p1" ? (x - plot.left) / plot.width : draft.x1,
      y1:
        draggingRef.current === "p1"
          ? yMax - ((y - plot.top) / plot.height) * (yMax - yMin)
          : draft.y1,
      x2: draggingRef.current === "p2" ? (x - plot.left) / plot.width : draft.x2,
      y2:
        draggingRef.current === "p2"
          ? yMax - ((y - plot.top) / plot.height) * (yMax - yMin)
          : draft.y2,
    });
  };

  const start = mapPoint({ x: 0, y: 0 });
  const end = mapPoint({ x: 1, y: 1 });
  const p1 = mapPoint({ x: draft.x1, y: draft.y1 });
  const p2 = mapPoint({ x: draft.x2, y: draft.y2 });
  const curvePath = buildCurvePath(draft, mapPoint);

  const handlePointerMove = (event: PointerEvent<SVGSVGElement>) => {
    if (!draggingRef.current) return;
    event.preventDefault();
    const next = unmapPointer(event);
    if (next) setDraft(next);
  };

  const endDrag = () => {
    if (!draggingRef.current) return;
    draggingRef.current = null;
    onCommit(draft);
  };

  const startDrag = (handle: "p1" | "p2", event: PointerEvent<SVGCircleElement>) => {
    event.preventDefault();
    event.stopPropagation();
    draggingRef.current = handle;
    event.currentTarget.setPointerCapture(event.pointerId);
  };

  return (
    <div className="overflow-hidden rounded-2xl border border-neutral-800 bg-black/40">
      <div className="flex items-center justify-between gap-3 border-b border-neutral-800 px-3 py-2">
        <div>
          <div className={LABEL}>CustomEase</div>
          <div className="mt-1 font-mono text-[10px] text-neutral-500">
            {serializeStudioCustomEaseData(draft)}
          </div>
        </div>
        <button
          type="button"
          onClick={() => {
            const reset = controlPointsForGsapEase("power3.out");
            setDraft(reset);
            onCommit(reset);
          }}
          className="inline-flex h-8 items-center justify-center gap-2 rounded-xl border border-neutral-800 bg-neutral-950 px-3 text-[10px] font-semibold uppercase tracking-[0.14em] text-neutral-400 transition-colors hover:border-neutral-700 hover:text-neutral-100"
        >
          <RotateCcw size={13} />
          Reset
        </button>
      </div>
      <svg
        ref={svgRef}
        viewBox={`0 0 ${width} ${height}`}
        className="block w-full select-none touch-none"
        onPointerMove={handlePointerMove}
        onPointerUp={endDrag}
        onPointerCancel={endDrag}
      >
        <rect x="0" y="0" width={width} height={height} fill="transparent" />
        {[0, 0.5, 1].map((value) => {
          const mapped = mapPoint({ x: 0, y: value });
          return (
            <g key={value}>
              <line
                x1={plot.left}
                x2={plot.left + plot.width}
                y1={mapped.y}
                y2={mapped.y}
                stroke="rgba(255,255,255,0.12)"
                strokeDasharray="5 8"
              />
              <text
                x={plot.left - 12}
                y={mapped.y + 4}
                textAnchor="end"
                className="fill-neutral-500 text-[10px] font-semibold"
              >
                {value}
              </text>
            </g>
          );
        })}
        <line
          x1={plot.left}
          x2={plot.left + plot.width}
          y1={plot.top + plot.height}
          y2={plot.top + plot.height}
          stroke="rgba(255,255,255,0.18)"
        />
        <line
          x1={plot.left}
          x2={plot.left}
          y1={plot.top}
          y2={plot.top + plot.height}
          stroke="rgba(255,255,255,0.18)"
        />
        <line x1={start.x} y1={start.y} x2={p1.x} y2={p1.y} stroke="rgba(255,221,87,0.34)" />
        <line x1={end.x} y1={end.y} x2={p2.x} y2={p2.y} stroke="rgba(255,221,87,0.34)" />
        <path d={curvePath} fill="none" stroke="#ffdd57" strokeWidth="4" strokeLinecap="round" />
        <circle cx={start.x} cy={start.y} r="5" fill="#ffdd57" />
        <circle cx={end.x} cy={end.y} r="5" fill="#ffdd57" />
        <circle
          cx={p1.x}
          cy={p1.y}
          r="9"
          fill="#141414"
          stroke="#ffdd57"
          strokeWidth="4"
          className="cursor-grab active:cursor-grabbing"
          onPointerDown={(event) => startDrag("p1", event)}
        />
        <circle
          cx={p2.x}
          cy={p2.y}
          r="9"
          fill="#141414"
          stroke="#ffdd57"
          strokeWidth="4"
          className="cursor-grab active:cursor-grabbing"
          onPointerDown={(event) => startDrag("p2", event)}
        />
        <text x={p1.x + 12} y={p1.y - 10} className="fill-neutral-400 text-[10px] font-semibold">
          P1
        </text>
        <text x={p2.x + 12} y={p2.y - 10} className="fill-neutral-400 text-[10px] font-semibold">
          P2
        </text>
      </svg>
      <div className="grid grid-cols-2 gap-2 border-t border-neutral-800 p-3">
        <div className="rounded-xl border border-neutral-800 bg-neutral-950 px-3 py-2 font-mono text-[10px] text-neutral-400">
          P1 {formatNumericValue(draft.x1)}, {formatNumericValue(draft.y1)}
        </div>
        <div className="rounded-xl border border-neutral-800 bg-neutral-950 px-3 py-2 font-mono text-[10px] text-neutral-400">
          P2 {formatNumericValue(draft.x2)}, {formatNumericValue(draft.y2)}
        </div>
      </div>
    </div>
  );
}
