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

export interface SidebarResizeBounds {
  min: number;
  max: number;
}

export interface SidebarResizeOptions {
  storageKey: string;
  defaultWidth: number;
  bounds: SidebarResizeBounds;
  side: "left" | "right";
  disabled: boolean;
  shellElement: HTMLElement | null;
  cssVariable: string;
}

export interface SidebarResizeState {
  width: number;
  setWidth(next: number, options?: { persist?: boolean }): number;
  onPointerDown(event: React.PointerEvent<HTMLDivElement>): void;
  onPointerMove(event: React.PointerEvent<HTMLDivElement>): void;
  onPointerUp(event: React.PointerEvent<HTMLDivElement>): void;
  onPointerCancel(event: React.PointerEvent<HTMLDivElement>): void;
  onKeyDown(event: React.KeyboardEvent<HTMLDivElement>): void;
  bounds: SidebarResizeBounds;
  isDragging: boolean;
  ariaValueNow: number;
  ariaValueText: string;
}

interface DragState {
  pointerId: number;
  startX: number;
  startWidth: number;
}

const NARROW_BREAKPOINT_PX = 1240;

export function useSidebarResize(options: SidebarResizeOptions): SidebarResizeState {
  const { storageKey, defaultWidth, bounds, side, disabled, shellElement, cssVariable } = options;
  const [width, setWidthState] = useState<number>(() => readStoredWidth(storageKey, defaultWidth, bounds));
  const [drag, setDrag] = useState<DragState | null>(null);
  const [dynamicBounds, setDynamicBounds] = useState<SidebarResizeBounds>(bounds);

  const applyWidth = useCallback((next: number) => {
    if (shellElement) {
      shellElement.style.setProperty(cssVariable, `${next}px`);
    }
    return next;
  }, [shellElement, cssVariable]);

  const setWidth = useCallback((next: number, options: { persist?: boolean } = {}) => {
    const target = clampWidth(next, dynamicBounds, defaultWidth);
    setWidthState(target);
    applyWidth(target);
    if (options.persist !== false) writeStoredWidth(storageKey, target);
    return target;
  }, [applyWidth, defaultWidth, dynamicBounds, storageKey]);

  useEffect(() => {
    if (disabled) return;
    applyWidth(width);
  }, [applyWidth, disabled, width]);

  useEffect(() => {
    if (disabled) return;
    const shell: HTMLElement | null = shellElement;
    if (!shell) return;
    function recompute(): void {
      const target: HTMLElement = shell as HTMLElement;
      const next = computeBounds(bounds, side, target, defaultWidth, width);
      setDynamicBounds(next);
      const clamped = clampWidth(width, next, defaultWidth);
      if (clamped !== width) {
        setWidthState(clamped);
        applyWidth(clamped);
      }
    }
    recompute();
    window.addEventListener("resize", recompute);
    return () => window.removeEventListener("resize", recompute);
  }, [applyWidth, bounds, defaultWidth, disabled, shellElement, side, width]);

  const onPointerDown = useCallback((event: React.PointerEvent<HTMLDivElement>) => {
    if (disabled) return;
    event.preventDefault();
    const target = event.currentTarget;
    target.setPointerCapture?.(event.pointerId);
    setDrag({ pointerId: event.pointerId, startX: event.clientX, startWidth: width });
  }, [disabled, width]);

  const onPointerMove = useCallback((event: React.PointerEvent<HTMLDivElement>) => {
    if (!drag || drag.pointerId !== event.pointerId) return;
    const delta = event.clientX - drag.startX;
    const direction = side === "right" ? -1 : 1;
    setWidth(drag.startWidth + delta * direction, { persist: false });
  }, [drag, setWidth, side]);

  const finishDrag = useCallback((event: React.PointerEvent<HTMLDivElement>) => {
    if (!drag || drag.pointerId !== event.pointerId) return;
    setDrag(null);
    writeStoredWidth(storageKey, width);
  }, [drag, storageKey, width]);

  const onPointerUp = useCallback((event: React.PointerEvent<HTMLDivElement>) => finishDrag(event), [finishDrag]);
  const onPointerCancel = useCallback((event: React.PointerEvent<HTMLDivElement>) => finishDrag(event), [finishDrag]);

  const onKeyDown = useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {
    if (disabled) return;
    const step = event.shiftKey ? 32 : 8;
    if (side === "right") {
      if (event.key === "ArrowLeft") { event.preventDefault(); setWidth(width + step); }
      else if (event.key === "ArrowRight") { event.preventDefault(); setWidth(width - step); }
    } else {
      if (event.key === "ArrowLeft") { event.preventDefault(); setWidth(width - step); }
      else if (event.key === "ArrowRight") { event.preventDefault(); setWidth(width + step); }
    }
    if (event.key === "Home") { event.preventDefault(); setWidth(dynamicBounds.min); }
    if (event.key === "End") { event.preventDefault(); setWidth(dynamicBounds.max); }
  }, [disabled, dynamicBounds.max, dynamicBounds.min, setWidth, side, width]);

  const ariaValueText = useMemo(() => side === "right" ? `右侧 HWPOD 面板宽度 ${width} 像素` : `Session sidebar 宽度 ${width} 像素`, [side, width]);

  return {
    width,
    setWidth,
    onPointerDown,
    onPointerMove,
    onPointerUp,
    onPointerCancel,
    onKeyDown,
    bounds: dynamicBounds,
    isDragging: drag !== null,
    ariaValueNow: width,
    ariaValueText
  };
}

function readStoredWidth(key: string, fallback: number, bounds: SidebarResizeBounds): number {
  if (typeof window === "undefined") return fallback;
  try {
    const raw = window.localStorage.getItem(key);
    const parsed = Number(raw);
    if (!Number.isFinite(parsed)) return fallback;
    return clampWidth(parsed, bounds, fallback);
  } catch {
    return fallback;
  }
}

function writeStoredWidth(key: string, value: number): void {
  if (typeof window === "undefined") return;
  try { window.localStorage.setItem(key, String(value)); } catch { /* ignore */ }
}

function clampWidth(value: number, bounds: SidebarResizeBounds, fallback: number): number {
  const numeric = Number.isFinite(Number(value)) ? Number(value) : fallback;
  return Math.min(Math.max(Math.round(numeric), bounds.min), bounds.max);
}

function computeBounds(base: SidebarResizeBounds, side: "left" | "right", shell: HTMLElement, fallback: number, currentWidth: number): SidebarResizeBounds {
  if (typeof window === "undefined") return base;
  if (window.matchMedia(`(max-width: ${NARROW_BREAKPOINT_PX}px)`).matches) {
    return { min: 0, max: Math.max(0, window.innerWidth) };
  }
  const railWidth = readCssPixel(shell, "--rail-width", 92);
  const collapsedRail = readCssPixel(shell, "--rail-collapsed-width", 44);
  const collapsed = shell.classList.contains("is-left-sidebar-collapsed");
  const effectiveRail = collapsed ? collapsedRail : railWidth;
  const sessionWidth = readCssPixel(shell, "--session-sidebar-width", 220);
  const minCenter = 420;
  if (side === "right") {
    const dynamicMax = Math.max(base.min, window.innerWidth - effectiveRail - sessionWidth - minCenter - 4);
    return { min: base.min, max: Math.min(base.max, dynamicMax) };
  }
  const rightWidth = currentWidth > 0 ? readCssPixel(shell, "--right-sidebar-width", currentWidth) : readCssPixel(shell, "--right-sidebar-width", 728);
  const dynamicMax = Math.max(base.min, window.innerWidth - effectiveRail - rightWidth - minCenter - 4);
  return { min: base.min, max: Math.min(base.max, dynamicMax) };
}

function readCssPixel(shell: HTMLElement, name: string, fallback: number): number {
  const raw = window.getComputedStyle(shell).getPropertyValue(name);
  const parsed = Number.parseFloat(raw);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}
