import { useCallback, useRef, useState, type PointerEvent } from "react";
import type { GutterInfo } from "./tree";

/**
 * A draggable gutter at a split boundary.
 *
 * `info.box` is where to render the strip (in container fractions 0–1);
 * `info.parentBox` is the parent split's bounding box, used to convert
 * pointer position back into a 0–1 ratio.
 *
 * The actual draggable hitbox is a few pixels thick (and centered on the
 * boundary), but we render a thin visible line via CSS pseudo-elements.
 */
const HITBOX_PX = 8;

export default function Gutter({
  info,
  containerRef,
  onRatioChange,
}: {
  info: GutterInfo;
  containerRef: React.RefObject<HTMLDivElement | null>;
  onRatioChange: (splitId: string, ratio: number) => void;
}) {
  const [dragging, setDragging] = useState(false);
  const draggingRef = useRef(false);

  const onPointerDown = useCallback((e: PointerEvent<HTMLDivElement>) => {
    (e.target as HTMLElement).setPointerCapture(e.pointerId);
    setDragging(true);
    draggingRef.current = true;
    e.preventDefault();
    e.stopPropagation();
  }, []);

  const onPointerMove = useCallback(
    (e: PointerEvent<HTMLDivElement>) => {
      if (!draggingRef.current || !containerRef.current) return;
      const rect = containerRef.current.getBoundingClientRect();
      if (rect.width <= 0 || rect.height <= 0) return;
      const xFrac = (e.clientX - rect.left) / rect.width;
      const yFrac = (e.clientY - rect.top) / rect.height;
      const pb = info.parentBox;
      const rawRatio =
        info.orientation === "h"
          ? (xFrac - pb.left) / pb.width
          : (yFrac - pb.top) / pb.height;
      const ratio = Math.max(0.05, Math.min(0.95, rawRatio));
      onRatioChange(info.splitId, ratio);
    },
    [containerRef, info, onRatioChange],
  );

  const onPointerUp = useCallback((e: PointerEvent<HTMLDivElement>) => {
    if (!draggingRef.current) return;
    (e.target as HTMLElement).releasePointerCapture(e.pointerId);
    draggingRef.current = false;
    setDragging(false);
  }, []);

  const isH = info.orientation === "h";
  // Visible 4px line, but the draggable hitbox is wider for grabbability.
  const halfHit = HITBOX_PX / 2;
  const style: React.CSSProperties = isH
    ? {
        position: "absolute",
        top: `${info.box.top * 100}%`,
        left: `calc(${info.box.left * 100}% - ${halfHit}px)`,
        height: `${info.box.height * 100}%`,
        width: `${HITBOX_PX}px`,
        cursor: "col-resize",
        zIndex: 10,
      }
    : {
        position: "absolute",
        top: `calc(${info.box.top * 100}% - ${halfHit}px)`,
        left: `${info.box.left * 100}%`,
        width: `${info.box.width * 100}%`,
        height: `${HITBOX_PX}px`,
        cursor: "row-resize",
        zIndex: 10,
      };

  return (
    <div
      className={`gutter ${isH ? "gutter-h" : "gutter-v"}${dragging ? " active" : ""}`}
      style={style}
      role="separator"
      aria-orientation={isH ? "vertical" : "horizontal"}
      aria-valuenow={Math.round(info.ratio * 100)}
      tabIndex={-1}
      onPointerDown={onPointerDown}
      onPointerMove={onPointerMove}
      onPointerUp={onPointerUp}
      onPointerCancel={onPointerUp}
    />
  );
}
