/**
 * DockRegion — a resizable, **in-flow** vertical column docked to the left or
 * right of the main area (ticket #22, gate G5).
 *
 * Unlike {@link FloatingWindow} (a modal overlay), a DockRegion is *not* an
 * overlay: it lives in the normal flex flow beside `<main>`, so docked views and
 * the terminal grid are visible at once (VS Code / JetBrains side-panel feel).
 * It therefore needs no z-index token — it never stacks over the main surface.
 *
 * Purely presentational: it owns only the drag mechanics of its width. The
 * caller controls `width` and commits new values via `onResize`, and supplies
 * the docked view(s) as children (each typically its own header + body). The
 * resize handle sits on the column's *inner* edge — the right edge for a
 * left-docked column, the left edge for a right-docked one — so dragging toward
 * the main area shrinks the column and away from it grows the column.
 *
 * Mounted at the chrome level (ProjectsView), never inside LayoutGrid/LeafView.
 */

import { useRef, type ReactNode } from "react";

import { cn } from "../lib/cn";

/** Which side of `<main>` the column is docked to. */
export type DockSide = "left" | "right";

/** Default clamp for the column width, in CSS pixels. */
const DEFAULT_MIN_WIDTH = 240;
const DEFAULT_MAX_WIDTH = 720;

export interface DockRegionProps {
  /** Side of the main area this column is docked to. */
  side: DockSide;
  /** Current column width in pixels (controlled by the caller). */
  width: number;
  /** Called live during the drag and on release with the clamped next width. */
  onResize: (nextWidth: number) => void;
  /** Minimum width in pixels (default 240). */
  minWidth?: number;
  /** Maximum width in pixels (default 720). */
  maxWidth?: number;
  /** Accessible label for the column region. */
  "aria-label"?: string;
  /** The docked view(s), stacked vertically. */
  children: ReactNode;
}

/**
 * A resizable docked column. The width is driven from a pointer drag on the
 * inner-edge handle, reported back through `onResize`; the caller holds the
 * value in state (ephemeral in V1 — persistence is a deferred follow-up).
 */
export function DockRegion({
  side,
  width,
  onResize,
  minWidth = DEFAULT_MIN_WIDTH,
  maxWidth = DEFAULT_MAX_WIDTH,
  "aria-label": ariaLabel,
  children,
}: DockRegionProps) {
  // Anchor the drag on pointer-down: remember where the pointer and the column
  // width started, then map the signed pointer delta to a width delta. A
  // left-docked column grows as the pointer moves right; a right-docked one
  // grows as it moves left (the handle is on the opposite, inner edge).
  const dragRef = useRef<{ startX: number; startWidth: number } | null>(null);

  function clamp(next: number): number {
    return Math.min(maxWidth, Math.max(minWidth, next));
  }

  function onPointerDown(e: React.PointerEvent<HTMLDivElement>) {
    e.preventDefault();
    // `setPointerCapture` is absent under jsdom; guard so tests can drive drags.
    e.currentTarget.setPointerCapture?.(e.pointerId);
    dragRef.current = { startX: e.clientX, startWidth: width };
  }
  function onPointerMove(e: React.PointerEvent<HTMLDivElement>) {
    const drag = dragRef.current;
    if (!drag) return;
    const delta = e.clientX - drag.startX;
    const signed = side === "left" ? delta : -delta;
    onResize(clamp(drag.startWidth + signed));
  }
  function onPointerUp(e: React.PointerEvent<HTMLDivElement>) {
    if (!dragRef.current) return;
    const delta = e.clientX - dragRef.current.startX;
    const signed = side === "left" ? delta : -delta;
    dragRef.current = null;
    onResize(clamp(width + signed));
  }

  const handle = (
    <div
      role="separator"
      aria-orientation="vertical"
      aria-label={`resize ${side} dock`}
      onPointerDown={onPointerDown}
      onPointerMove={onPointerMove}
      onPointerUp={onPointerUp}
      className="shrink-0 cursor-col-resize bg-border hover:bg-border-strong"
      style={{ flex: "0 0 4px", touchAction: "none" }}
    />
  );

  return (
    <aside
      aria-label={ariaLabel ?? `${side} dock`}
      className={cn(
        "flex h-full shrink-0 bg-surface",
        // Children come first in the DOM; the flex direction places the handle
        // on the inner edge: right of a left dock, left of a right dock.
        side === "left"
          ? "flex-row border-r border-border"
          : "flex-row-reverse border-l border-border",
      )}
      style={{ width }}
    >
      <div className="flex min-w-0 flex-1 flex-col overflow-hidden">
        {children}
      </div>
      {handle}
    </aside>
  );
}
