import type { CSSProperties, ComponentPropsWithoutRef, PointerEvent as ReactPointerEvent, ReactNode } from 'react';
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
import type { LucideIcon } from 'lucide-react';
import { ChevronDown, ChevronUp, Minus, Plus, X } from 'lucide-react';
import { cn } from 'helpers/cn';
import { Card } from '@/components/Card';

type Position = { x: number; y: number };

type WidgetProps = ComponentPropsWithoutRef<'div'> & {
  title?: string;
  resizable?: boolean;
  collapsible?: boolean | { title: string; icon?: LucideIcon };
  minimizable?: boolean;
  moveable?: boolean;
  position?: Position;
  onPositionChange?: (pos: Position) => void;
  onClose?: () => void;
};

export const Widget = ({ title, className, style, resizable, collapsible, minimizable = false, moveable, position: controlledPosition, onPositionChange, onClose, children, ...props }: WidgetProps) => {
  const [expanded, setExpanded] = useState(true);
  const [minimized, setMinimized] = useState(false);
  const [internalPosition, setInternalPosition] = useState<Position>({ x: 0, y: 0 });
  const isControlled = controlledPosition !== undefined;
  const position = isControlled ? controlledPosition : internalPosition;
  const setPosition = isControlled ? (pos: Position | ((prev: Position) => Position)) => {
    const next = typeof pos === 'function' ? pos(controlledPosition) : pos;
    onPositionChange?.(next);
  } : setInternalPosition;
  const cardRef = useRef<HTMLDivElement>(null);
  const dragRef = useRef<{
    startX: number;
    startY: number;
    originX: number;
    originY: number;
    naturalLeft: number;
    naturalTop: number;
    cardWidth: number;
    cardHeight: number;
  } | null>(null);
  const preToggleRect = useRef<{ left: number; top: number } | null>(null);

  const HEADER_HEIGHT = 64;

  const toggleMinimized = useCallback(() => {
    if (cardRef.current) {
      const rect = cardRef.current.getBoundingClientRect();
      preToggleRect.current = { left: rect.left, top: rect.top };
    }
    setMinimized((v) => !v);
  }, []);

  useLayoutEffect(() => {
    if (!cardRef.current || !preToggleRect.current) return;
    const prev = preToggleRect.current;
    preToggleRect.current = null;
    const newRect = cardRef.current.getBoundingClientRect();
    setPosition((p) => ({
      x: p.x + (prev.left - newRect.left),
      y: p.y + (prev.top - newRect.top),
    }));
  }, [minimized]);

  const onPointerDown = useCallback(
    (ev: ReactPointerEvent) => {
      if (!moveable) return;
      const target = ev.target as HTMLElement;
      const card = ev.currentTarget as HTMLElement;
      const isCardPadding = target === card;
      const isHeader = !isCardPadding && target.closest('[data-widget-header]') && !target.closest('button');
      if (!isCardPadding && !isHeader) return;
      if (minimizable && isHeader && ev.detail === 2) {
        toggleMinimized();
        return;
      }
      const rect = card.getBoundingClientRect();
      dragRef.current = {
        startX: ev.clientX,
        startY: ev.clientY,
        originX: position.x,
        originY: position.y,
        naturalLeft: rect.left - position.x,
        naturalTop: rect.top - position.y,
        cardWidth: rect.width,
        cardHeight: rect.height,
      };
      card.setPointerCapture(ev.pointerId);
    },
    [moveable, position],
  );

  const onPointerMove = useCallback((ev: ReactPointerEvent) => {
    if (!dragRef.current) return;
    const d = dragRef.current;
    const dx = ev.clientX - d.startX;
    const dy = ev.clientY - d.startY;
    const newX = d.originX + dx;
    const newY = d.originY + dy;
    const vw = window.innerWidth;
    const vh = window.innerHeight;
    setPosition({
      x: Math.min(Math.max(newX, -d.naturalLeft), vw - d.naturalLeft - d.cardWidth),
      y: Math.min(Math.max(newY, HEADER_HEIGHT - d.naturalTop), vh - d.naturalTop - d.cardHeight),
    });
  }, []);

  const onPointerUp = useCallback(() => {
    dragRef.current = null;
  }, []);

  const resizableStyle: CSSProperties | undefined = resizable ? { resize: 'both', overflow: 'auto' } : undefined;
  const moveableStyle: CSSProperties | undefined = moveable
    ? { position: 'relative', transform: `translate(${position.x}px, ${position.y}px)` }
    : undefined;

  return (
    <Card
      ref={cardRef}
      className={cn(
        'relative p-0',
        collapsible && 'pt-0',
        moveable && 'cursor-grab [&>*]:cursor-auto',
        className,
        minimized && '!h-auto !w-auto',
      )}
      style={{ ...resizableStyle, ...moveableStyle, ...style }}
      onPointerDown={moveable ? onPointerDown : undefined}
      onPointerMove={moveable ? onPointerMove : undefined}
      onPointerUp={moveable ? onPointerUp : undefined}
      {...props}
    >
      <div data-widget-header className="flex h-8 items-center gap-12 px-3 select-none cursor-grab">
        {title && <span className="text-sm font-bold text-muted-foreground pointer-events-none">{title}</span>}
        <div className="ml-auto flex items-center gap-0.5">
          {minimizable && (
            <button
              type="button"
              className="p-1 rounded cursor-pointer text-muted-foreground hover:text-foreground"
              onClick={() => toggleMinimized()}
            >
              {minimized ? <Plus size={14} /> : <Minus size={14} />}
            </button>
          )}
          {onClose && (
            <button
              type="button"
              className="p-1 rounded cursor-pointer text-muted-foreground hover:text-foreground"
              onClick={onClose}
            >
              <X size={14} />
            </button>
          )}
        </div>
      </div>
      {!minimized && (
        <>
          {collapsible && (
            <button
              type="button"
              className="flex w-full items-center gap-2 p-3 cursor-pointer"
              onClick={() => setExpanded((v) => !v)}
            >
              {typeof collapsible === 'object' && collapsible.icon && (
                <collapsible.icon size={16} className="text-muted-foreground" />
              )}
              {typeof collapsible === 'object' && collapsible.title && (
                <span className="text-sm text-muted-foreground">{collapsible.title}</span>
              )}
              <span className="ml-auto text-muted-foreground">
                {expanded ? <ChevronDown size={16} /> : <ChevronUp size={16} />}
              </span>
            </button>
          )}
          {collapsible ? expanded && <div className="px-5 pb-5">{children}</div> : children}
        </>
      )}
    </Card>
  );
};
