import { useMemo, useState, useCallback, type DragEvent } from 'react';
import { X, Plus, RotateCcw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useDock } from 'officerdev';
import { useCapabilities } from 'hooks/useCapabilities';
import { CORE_DOCK_ITEMS, dockItemsFromPlugins, DEFAULT_DOCK_PATHS } from '@/Screens/Dashboard/Layout/Dock';

type DockPillProps = {
  label: string;
  path: string;
  color: string;
  visible: boolean;
  onAction: (path: string) => void;
  onDragStart: (ev: DragEvent, path: string) => void;
  onDropOnPill?: (draggedPath: string) => void;
  dropIndicator?: 'left' | 'right' | null;
  onDragOverPill?: (ev: DragEvent) => void;
  onDragLeavePill?: () => void;
};

const DockPill = ({
  label,
  path,
  color,
  visible,
  onAction,
  onDragStart,
  onDropOnPill,
  dropIndicator,
  onDragOverPill,
  onDragLeavePill,
}: DockPillProps) => (
  <span
    draggable
    onDragStart={(ev) => onDragStart(ev, path)}
    onDragOver={onDragOverPill}
    onDragLeave={onDragLeavePill}
    onDrop={
      onDropOnPill
        ? (ev) => {
            ev.preventDefault();
            ev.stopPropagation();
            const p = ev.dataTransfer.getData('text/plain');
            if (p) onDropOnPill(p);
          }
        : undefined
    }
    className={`relative inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium cursor-grab active:cursor-grabbing select-none border border-duck-dark/15 dark:border-foreground/15 bg-background/60 text-duck-dark/80 dark:text-foreground/80 hover:bg-duck-dark/5 dark:hover:bg-foreground/5 transition-colors ${dropIndicator === 'left' ? 'ring-l-2 ring-duck-teal' : ''} ${dropIndicator === 'right' ? 'ring-r-2 ring-duck-teal' : ''}`}
  >
    {dropIndicator === 'left' && <span className="absolute -left-1 top-1 bottom-1 w-0.5 rounded-full bg-duck-teal" />}
    <span className="w-2 h-2 rounded-full shrink-0" style={{ background: color }} />
    {label}
    <button
      onClick={() => onAction(path)}
      className="ml-0.5 p-0.5 rounded-full hover:bg-duck-dark/10 dark:hover:bg-foreground/10 cursor-pointer transition-colors"
    >
      {visible ? <X className="h-3 w-3" /> : <Plus className="h-3 w-3" />}
    </button>
    {dropIndicator === 'right' && <span className="absolute -right-1 top-1 bottom-1 w-0.5 rounded-full bg-duck-teal" />}
  </span>
);

type DropZoneProps = {
  label: string;
  children: React.ReactNode;
  onDrop: (path: string) => void;
};

const DropZone = ({ label, children, onDrop }: DropZoneProps) => {
  const [over, setOver] = useState(false);

  const handleDragOver = useCallback((ev: DragEvent) => {
    ev.preventDefault();
    setOver(true);
  }, []);

  const handleDragLeave = useCallback(() => setOver(false), []);

  const handleDrop = useCallback(
    (ev: DragEvent) => {
      ev.preventDefault();
      setOver(false);
      const path = ev.dataTransfer.getData('text/plain');
      if (path) onDrop(path);
    },
    [onDrop],
  );

  return (
    <div className="grid gap-1.5">
      <span className="text-xs font-medium text-duck-dark/50 dark:text-foreground/50 uppercase tracking-wide">
        {label}
      </span>
      <div
        onDragOver={handleDragOver}
        onDragLeave={handleDragLeave}
        onDrop={handleDrop}
        className={`min-h-[48px] p-2 rounded-lg border border-dashed transition-colors flex flex-wrap gap-1.5 ${over ? 'border-duck-teal bg-duck-teal/5' : 'border-duck-dark/15 dark:border-foreground/15'}`}
      >
        {children}
      </div>
    </div>
  );
};

export const DockSettings = () => {
  // Same composition as the dock itself. Offering a pin for an uninstalled feature would let someone
  // pin a tile that cannot appear, which reads as the setting being broken.
  const { plugins } = useCapabilities();
  const allDockItems = useMemo(() => [...CORE_DOCK_ITEMS, ...dockItemsFromPlugins(plugins)], [plugins]);
  const { items, allItems, setItems, reset } = useDock(allDockItems, DEFAULT_DOCK_PATHS);
  const [dropTarget, setDropTarget] = useState<{ path: string; side: 'left' | 'right' } | null>(null);

  const visiblePaths = new Set(items.map((i) => i.to));
  const hiddenItems = allItems.filter((i) => !visiblePaths.has(i.to));

  const onDragStart = useCallback((ev: DragEvent, path: string) => {
    ev.dataTransfer.setData('text/plain', path);
    ev.dataTransfer.effectAllowed = 'move';
  }, []);

  const addItem = useCallback(
    (path: string) => {
      if (visiblePaths.has(path)) return;
      setItems([...items.map((i) => i.to), path]);
    },
    [items, visiblePaths, setItems],
  );

  const removeItem = useCallback(
    (path: string) => {
      setItems(items.filter((i) => i.to !== path).map((i) => i.to));
    },
    [items, setItems],
  );

  const insertAt = useCallback(
    (draggedPath: string, targetPath: string, side: 'left' | 'right') => {
      const paths = items.filter((i) => i.to !== draggedPath).map((i) => i.to);
      const targetIdx = paths.indexOf(targetPath);
      if (targetIdx === -1) return;
      const insertIdx = side === 'left' ? targetIdx : targetIdx + 1;
      paths.splice(insertIdx, 0, draggedPath);
      setItems(paths);
      setDropTarget(null);
    },
    [items, setItems],
  );

  const handlePillDragOver = useCallback((ev: DragEvent, pillPath: string) => {
    ev.preventDefault();
    ev.dataTransfer.dropEffect = 'move';
    const rect = (ev.currentTarget as HTMLElement).getBoundingClientRect();
    const midX = rect.left + rect.width / 2;
    const side = ev.clientX < midX ? 'left' : 'right';
    setDropTarget((prev) => (prev?.path === pillPath && prev?.side === side ? prev : { path: pillPath, side }));
  }, []);

  const handlePillDragLeave = useCallback(() => setDropTarget(null), []);

  const handlePillDrop = useCallback(
    (draggedPath: string, targetPath: string) => {
      if (draggedPath === targetPath) {
        setDropTarget(null);
        return;
      }
      const side = dropTarget?.path === targetPath ? dropTarget.side : 'right';
      insertAt(draggedPath, targetPath, side);
    },
    [dropTarget, insertAt],
  );

  const onDropVisible = useCallback(
    (path: string) => {
      setDropTarget(null);
      if (visiblePaths.has(path)) return;
      addItem(path);
    },
    [visiblePaths, addItem],
  );

  const onDropHidden = useCallback(
    (path: string) => {
      setDropTarget(null);
      if (!visiblePaths.has(path)) return;
      removeItem(path);
    },
    [visiblePaths, removeItem],
  );

  return (
    <div className="grid gap-4">
      <DropZone label="Visible" onDrop={onDropVisible}>
        {items.length === 0 && (
          <span className="text-xs text-duck-dark/30 dark:text-foreground/30 py-1">
            Drag items here to show in dock
          </span>
        )}
        {items.map((item) => (
          <DockPill
            key={item.to}
            label={item.label}
            path={item.to}
            color={item.color}
            visible
            onAction={removeItem}
            onDragStart={onDragStart}
            dropIndicator={dropTarget?.path === item.to ? dropTarget.side : null}
            onDragOverPill={(ev) => handlePillDragOver(ev, item.to)}
            onDragLeavePill={handlePillDragLeave}
            onDropOnPill={(draggedPath) => handlePillDrop(draggedPath, item.to)}
          />
        ))}
      </DropZone>

      <DropZone label="Hidden" onDrop={onDropHidden}>
        {hiddenItems.length === 0 && (
          <span className="text-xs text-duck-dark/30 dark:text-foreground/30 py-1">All items visible</span>
        )}
        {hiddenItems.map((item) => (
          <DockPill
            key={item.to}
            label={item.label}
            path={item.to}
            color={item.color}
            visible={false}
            onAction={addItem}
            onDragStart={onDragStart}
          />
        ))}
      </DropZone>

      <Button type="button" variant="outline" onClick={reset} className="w-full h-9 text-sm cursor-pointer">
        <RotateCcw className="h-3.5 w-3.5 mr-1.5" />
        Reset to defaults
      </Button>
    </div>
  );
};
