"use client";

import React, {
  useCallback,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import {
  useDndContext,
  useDroppable,
  useDndMonitor,
  type DragEndEvent,
  type DragStartEvent,
  type DragOverEvent,
} from "@dnd-kit/core";
import {
  useSortable,
  SortableContext,
  verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import {
  ChevronDown,
  ChevronRight,
  GripVertical,
  Plus,
  Trash2,
  GitBranch,
  Edit3,
  CornerDownRight,
  Repeat,
} from "lucide-react";
import { cn } from "~/lib/utils";
import {
  type ExperimentStep,
  type ExperimentAction,
} from "~/lib/experiment-designer/types";
import { useDesignerStore } from "../state/store";
import { actionRegistry } from "../ActionRegistry";
import { Button } from "~/components/ui/button";
import { Badge } from "~/components/ui/badge";
import { Input } from "~/components/ui/input";
import { SortableActionChip } from "./ActionChip";

/**
 * FlowWorkspace
 *
 * Virtualized step + action workspace with local (Option B) sortable handling.
 * Reordering is processed locally via useDndMonitor (not in DesignerRoot)
 * to keep orchestration layer simpler and reduce cross-component coupling.
 *
 * Features:
 *  - Virtualized step list (absolute positioned variable heights)
 *  - Inline step rename
 *  - Step & action creation / deletion
 *  - Step and action reordering (drag handles)
 *  - Drag-from-library action insertion (handled by root DnD; droppables here)
 *  - Empty step drop affordance + highlight
 *
 * Sortable ID strategy (to avoid collision with palette action ids):
 *  - Sortable Step:   s-step-<stepId>
 *  - Sortable Action: s-act-<actionId>
 *  - Droppable Step:  step-<stepId> (kept for root palette drops)
 */

interface FlowWorkspaceProps {
  className?: string;
  overscan?: number;
  onStepCreate?: (step: ExperimentStep) => void;
  onStepDelete?: (stepId: string) => void;
  onActionCreate?: (stepId: string, action: ExperimentAction) => void;
}

export interface VirtualItem {
  index: number;
  top: number;
  height: number;
  step: ExperimentStep;
  key: string;
  visible: boolean;
}

interface StepRowProps {
  item: VirtualItem;
  step: ExperimentStep; // Explicit pass for freshness
  totalSteps: number;
  selectedStepId: string | null | undefined;
  selectedActionId: string | null | undefined;
  renamingStepId: string | null;
  onSelectStep: (id: string | undefined) => void;
  onSelectAction: (stepId: string, actionId: string | undefined) => void;
  onToggleExpanded: (step: ExperimentStep) => void;
  onRenameStep: (step: ExperimentStep, newName: string) => void;
  onDeleteStep: (step: ExperimentStep) => void;
  onDeleteAction: (stepId: string, actionId: string) => void;
  setRenamingStepId: (id: string | null) => void;
  registerMeasureRef: (stepId: string, el: HTMLDivElement | null) => void;
  onReorderStep: (stepId: string, direction: "up" | "down") => void;
  onReorderAction?: (
    stepId: string,
    actionId: string,
    direction: "up" | "down",
  ) => void;
  isChild?: boolean;
}

function StepRow({
  item,
  step,
  totalSteps,
  selectedStepId,
  selectedActionId,
  renamingStepId,
  onSelectStep,
  onSelectAction,
  onToggleExpanded,
  onRenameStep,
  onDeleteStep,
  onDeleteAction,
  setRenamingStepId,
  registerMeasureRef,
  onReorderStep,
  onReorderAction,
  isChild,
}: StepRowProps) {
  // const step = item.step; // Removed local derivation
  const allSteps = useDesignerStore((s) => s.steps);
  const insertionProjection = useDesignerStore((s) => s.insertionProjection);

  const displayActions = useMemo(() => {
    if (
      insertionProjection?.stepId === step.id &&
      insertionProjection.parentId === null
    ) {
      const copy = [...step.actions];
      // Insert placeholder action
      // Ensure specific ID doesn't crash keys if collision (collision unlikely for library items)
      // Actually, standard array key is action.id.
      copy.splice(insertionProjection.index, 0, insertionProjection.action);
      return copy;
    }
    return step.actions;
  }, [step.actions, step.id, insertionProjection]);

  const style: React.CSSProperties = {
    position: "absolute",
    top: item.top,
    left: 0,
    right: 0,
    width: "100%",
    transition: "top 300ms cubic-bezier(0.4, 0, 0.2, 1)",
    // transform: CSS.Transform.toString(transform), // Removed
    // zIndex: isDragging ? 25 : undefined,
  };

  return (
    <div style={style} data-step-id={step.id}>
      <div
        ref={(el) => registerMeasureRef(step.id, el)}
        className={cn(
          "relative px-3 py-4 transition-all duration-300",
          isChild && "ml-8 pl-0",
        )}
        data-step-id={step.id}
      >
        {isChild && (
          <div className="text-muted-foreground/40 absolute top-8 left-[-24px]">
            <CornerDownRight className="h-5 w-5" />
          </div>
        )}
        <StepDroppableArea stepId={step.id} />
        <div
          className={cn(
            "mb-2 rounded-lg border shadow-sm transition-colors",
            selectedStepId === step.id
              ? "border-border bg-accent/30"
              : "hover:bg-accent/30",
          )}
        >
          <div
            className="flex items-center justify-between gap-2 border-b px-2 py-1.5"
            onClick={(e) => {
              const tag = (e.target as HTMLElement).tagName.toLowerCase();
              if (tag === "input" || tag === "textarea" || tag === "button")
                return;
              onSelectStep(step.id);
              onSelectAction(step.id, undefined);
            }}
            role="button"
            tabIndex={0}
          >
            <div className="flex items-center gap-2">
              <button
                type="button"
                onClick={(e) => {
                  e.stopPropagation();
                  onToggleExpanded(step);
                }}
                className="text-muted-foreground hover:bg-accent/60 hover:text-foreground rounded p-1"
                aria-label={step.expanded ? "Collapse step" : "Expand step"}
              >
                {step.expanded ? (
                  <ChevronDown className="h-4 w-4" />
                ) : (
                  <ChevronRight className="h-4 w-4" />
                )}
              </button>
              <Badge
                variant="outline"
                className="h-5 px-1.5 text-[10px] font-normal"
              >
                {step.order + 1}
              </Badge>
              {renamingStepId === step.id ? (
                <Input
                  autoFocus
                  defaultValue={step.name}
                  className="h-7 w-40 text-xs"
                  onClick={(e) => e.stopPropagation()}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") {
                      onRenameStep(
                        step,
                        (e.target as HTMLInputElement).value.trim() ||
                          step.name,
                      );
                      setRenamingStepId(null);
                    } else if (e.key === "Escape") {
                      setRenamingStepId(null);
                    }
                  }}
                  onBlur={(e) => {
                    onRenameStep(step, e.target.value.trim() || step.name);
                    setRenamingStepId(null);
                  }}
                />
              ) : (
                <div className="flex items-center gap-1">
                  <span className="text-sm font-medium">{step.name}</span>
                  <button
                    type="button"
                    className="text-muted-foreground hover:text-foreground p-1 opacity-0 group-hover:opacity-100"
                    aria-label="Rename step"
                    onClick={(e) => {
                      e.stopPropagation();
                      setRenamingStepId(step.id);
                    }}
                  >
                    <Edit3 className="h-3.5 w-3.5" />
                  </button>
                </div>
              )}
              <span className="text-muted-foreground hidden text-[11px] md:inline">
                {step.actions.length} actions
              </span>
            </div>
            <div className="flex items-center gap-1">
              <Button
                variant="ghost"
                size="sm"
                className="h-7 w-7 p-0 text-[11px] text-red-500 hover:text-red-600"
                onClick={(e) => {
                  e.stopPropagation();
                  onDeleteStep(step);
                }}
                aria-label="Delete step"
              >
                <Trash2 className="h-3.5 w-3.5" />
              </Button>
              <Button
                variant="ghost"
                size="sm"
                className="text-muted-foreground hover:text-foreground h-7 w-7 p-0 text-[11px]"
                onClick={(e) => {
                  e.stopPropagation();
                  onReorderStep(step.id, "up");
                }}
                disabled={item.index === 0}
                aria-label="Move step up"
              >
                <ChevronRight className="h-4 w-4 -rotate-90" />
              </Button>
              <Button
                variant="ghost"
                size="sm"
                className="text-muted-foreground hover:text-foreground h-7 w-7 p-0 text-[11px]"
                onClick={(e) => {
                  e.stopPropagation();
                  onReorderStep(step.id, "down");
                }}
                disabled={item.index === totalSteps - 1}
                aria-label="Move step down"
              >
                <ChevronRight className="h-4 w-4 rotate-90" />
              </Button>
            </div>
          </div>

          {/* Conditional Branching Visualization */}

          {/* Loop Visualization */}
          {step.type === "loop" && (
            <div
              className="mx-3 my-3 rounded-md border text-xs"
              style={{
                backgroundColor: "var(--validation-info-bg, #f0f9ff)",
                borderColor: "var(--validation-info-border, #bae6fd)",
              }}
            >
              <div
                className="flex items-center gap-2 border-b px-3 py-2 font-medium"
                style={{
                  borderColor: "var(--validation-info-border, #bae6fd)",
                  color: "var(--validation-info-text, #0369a1)",
                }}
              >
                <Repeat className="h-3.5 w-3.5" />
                <span>Loop Logic</span>
              </div>

              <div className="space-y-2 p-2">
                <div className="flex items-center gap-2 text-[11px]">
                  <span className="text-muted-foreground">Repeat:</span>
                  <Badge variant="outline" className="font-mono">
                    {(step.trigger.conditions as any).loop?.iterations || 1}{" "}
                    times
                  </Badge>
                </div>
                <div className="flex items-center gap-2 text-[11px]">
                  <span className="text-muted-foreground">Approval:</span>
                  <Badge
                    variant={
                      (step.trigger.conditions as any).loop?.requireApproval !==
                      false
                        ? "default"
                        : "secondary"
                    }
                  >
                    {(step.trigger.conditions as any).loop?.requireApproval !==
                    false
                      ? "Required"
                      : "Auto-proceed"}
                  </Badge>
                </div>
              </div>
            </div>
          )}

          {/* Action List (Collapsible/Virtual content) */}
          {step.expanded && (
            <div className="bg-background/40 min-h-[3rem] space-y-2 p-2 pb-8">
              <SortableContext
                items={displayActions.map((a) => sortableActionId(a.id))}
                strategy={verticalListSortingStrategy}
              >
                <div className="flex w-full flex-col gap-2">
                  {displayActions.length === 0 ? (
                    <div className="text-muted-foreground flex h-12 items-center justify-center rounded border border-dashed text-xs">
                      Drop actions here
                    </div>
                  ) : (
                    displayActions.map((action, index) => (
                      <SortableActionChip
                        key={action.id}
                        stepId={step.id}
                        action={action}
                        parentId={null}
                        selectedActionId={selectedActionId}
                        onSelectAction={onSelectAction}
                        onDeleteAction={onDeleteAction}
                        onReorderAction={onReorderAction}
                        isFirst={index === 0}
                        isLast={index === displayActions.length - 1}
                      />
                    ))
                  )}
                </div>
              </SortableContext>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* -------------------------------------------------------------------------- */
/* Step Card Preview (for DragOverlay)                                         */
/* -------------------------------------------------------------------------- */

export function StepCardPreview({
  step,
  dragHandle,
}: {
  step: ExperimentStep;
  dragHandle?: boolean;
}) {
  return (
    <div
      className={cn(
        "bg-background rounded-lg border shadow-xl ring-2 ring-blue-500/20",
        dragHandle && "cursor-grabbing",
      )}
    >
      <div className="flex items-center justify-between gap-2 border-b p-3 px-2 py-1.5">
        <div className="flex items-center gap-2">
          <div className="text-muted-foreground rounded p-1">
            <ChevronRight className="h-4 w-4" />
          </div>
          <Badge
            variant="outline"
            className="h-5 px-1.5 text-[10px] font-normal"
          >
            {step.order + 1}
          </Badge>
          <div className="flex items-center gap-1">
            <span className="text-sm font-medium">{step.name}</span>
          </div>
          <span className="text-muted-foreground hidden text-[11px] md:inline">
            {step.actions.length} actions
          </span>
        </div>
        <div className="text-muted-foreground flex items-center gap-1">
          <GripVertical className="h-4 w-4" />
        </div>
      </div>
      {/* Preview optional: show empty body hint or just the header? Header is usually enough for sorting. */}
      <div className="bg-muted/10 flex h-12 items-center justify-center border-t border-dashed p-2">
        <span className="text-muted-foreground text-[10px]">
          {step.actions.length} actions hidden while dragging
        </span>
      </div>
    </div>
  );
}

/* -------------------------------------------------------------------------- */
/* Utility                                                                    */
/* -------------------------------------------------------------------------- */

function generateStepId(): string {
  return `step-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
}

function sortableStepId(stepId: string) {
  return `s-step-${stepId}`;
}
function sortableActionId(actionId: string) {
  return `s-act-${actionId}`;
}
function parseSortableStep(id: string): string | null {
  return id.startsWith("s-step-") ? id.slice("s-step-".length) : null;
}
function parseSortableAction(id: string): string | null {
  return id.startsWith("s-act-") ? id.slice("s-act-".length) : null;
}

/* -------------------------------------------------------------------------- */
/* Droppable Overlay (for palette action drops)                               */
/* -------------------------------------------------------------------------- */
function StepDroppableArea({ stepId }: { stepId: string }) {
  const { active } = useDndContext();
  const isStepDragging = active?.id.toString().startsWith("s-step-");

  const { isOver, setNodeRef } = useDroppable({
    id: `step-${stepId}`,
    disabled: isStepDragging,
  });

  if (isStepDragging) return null;

  return (
    <div
      ref={setNodeRef}
      data-step-drop
      className={cn(
        "pointer-events-none absolute inset-0 rounded-md transition-colors",
        isOver &&
          "bg-blue-50/40 ring-2 ring-blue-400/60 ring-offset-0 dark:bg-blue-950/20",
      )}
    />
  );
}

/* -------------------------------------------------------------------------- */
/* FlowWorkspace Component                                                     */
/* -------------------------------------------------------------------------- */

export function FlowWorkspace({
  className,
  overscan = 400,
  onStepCreate,
  onStepDelete,
  onActionCreate: _onActionCreate,
}: FlowWorkspaceProps) {
  /* Store selectors */
  const steps = useDesignerStore((s) => s.steps);
  const selectStep = useDesignerStore((s) => s.selectStep);
  const selectAction = useDesignerStore((s) => s.selectAction);
  const selectedStepId = useDesignerStore((s) => s.selectedStepId);
  const selectedActionId = useDesignerStore((s) => s.selectedActionId);

  const upsertStep = useDesignerStore((s) => s.upsertStep);
  const removeStep = useDesignerStore((s) => s.removeStep);

  const removeAction = useDesignerStore((s) => s.removeAction);
  const reorderStep = useDesignerStore((s) => s.reorderStep);
  const moveAction = useDesignerStore((s) => s.moveAction);
  const recomputeHash = useDesignerStore((s) => s.recomputeHash);

  /* Local state */
  const containerRef = useRef<HTMLDivElement | null>(null);
  const measureRefs = useRef<Map<string, HTMLDivElement>>(new Map());
  const roRef = useRef<ResizeObserver | null>(null);
  const pendingHeightsRef = useRef<Map<string, number> | null>(null);
  const heightsRafRef = useRef<number | null>(null);
  const [heights, setHeights] = useState<Map<string, number>>(new Map());
  const [scrollTop, setScrollTop] = useState(0);
  const [viewportHeight, setViewportHeight] = useState(600);
  const [renamingStepId, setRenamingStepId] = useState<string | null>(null);
  // dragKind state removed (unused after refactor)

  /* Parent lookup for action reorder */
  const actionParentMap = useMemo(() => {
    const map = new Map<string, string>();
    for (const step of steps) {
      for (const a of step.actions) {
        map.set(a.id, step.id);
      }
    }
    return map;
  }, [steps]);

  /* Hierarchy detection for visual indentation */
  const childStepIds = useMemo(() => {
    const children = new Set<string>();
    for (const step of steps) {
      if (
        step.type === "conditional" &&
        (step.trigger.conditions as any)?.options
      ) {
        for (const opt of (step.trigger.conditions as any).options) {
          if (opt.nextStepId) {
            children.add(opt.nextStepId);
          }
        }
      }
    }
    return children;
  }, [steps]);

  /* Resize observer for viewport and width changes */
  useLayoutEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const observer = new ResizeObserver((entries) => {
      for (const entry of entries) {
        const cr = entry.contentRect;
        setViewportHeight(cr.height);
        // Do not invalidate all heights on width change; per-step observers will update as needed
      }
    });
    observer.observe(el);

    setViewportHeight(el.clientHeight);
    return () => observer.disconnect();
  }, []);

  /* Per-step measurement observer (attach/detach on ref set) */
  useLayoutEffect(() => {
    roRef.current = new ResizeObserver((entries) => {
      pendingHeightsRef.current ??= new Map();
      for (const entry of entries) {
        const id = entry.target.getAttribute("data-step-id");
        if (!id) continue;
        const h = entry.contentRect.height;
        pendingHeightsRef.current.set(id, h);
      }
      heightsRafRef.current ??= requestAnimationFrame(() => {
        const pending = pendingHeightsRef.current;
        heightsRafRef.current = null;
        pendingHeightsRef.current = null;
        if (!pending) return;
        setHeights((prev) => {
          let changed = false;
          const next = new Map(prev);
          for (const [id, h] of pending) {
            if (prev.get(id) !== h) {
              next.set(id, h);
              changed = true;
            }
          }
          return changed ? next : prev;
        });
      });
    });
    return () => {
      if (heightsRafRef.current) cancelAnimationFrame(heightsRafRef.current);
      heightsRafRef.current = null;
      pendingHeightsRef.current = null;
      roRef.current?.disconnect();
      roRef.current = null;
    };
  }, []);

  /* Scroll */
  const onScroll = useCallback(() => {
    if (!containerRef.current) return;
    setScrollTop(containerRef.current.scrollTop);
  }, []);

  /* Virtual items */
  const estimatedBaseHeight = 140;
  const virtualItems: VirtualItem[] = useMemo(() => {
    const out: VirtualItem[] = [];
    let offset = 0;
    steps.forEach((step, idx) => {
      const h = heights.get(step.id) ?? estimatedBaseHeight;
      const top = offset;
      const visible =
        top + h > scrollTop - overscan &&
        top < scrollTop + viewportHeight + overscan;
      out.push({
        index: idx,
        top,
        height: h,
        step,
        key: step.id,
        visible,
      });
      offset += h;
    });
    return out;
  }, [steps, heights, scrollTop, viewportHeight, overscan]);

  const totalHeight = useMemo(
    () =>
      steps.reduce(
        (sum, step) => sum + (heights.get(step.id) ?? estimatedBaseHeight),
        0,
      ),
    [steps, heights],
  );

  /* CRUD Helpers */
  const createStep = useCallback(
    (insertIndex?: number) => {
      const newStep: ExperimentStep = {
        id: generateStepId(),
        name: `Step ${steps.length + 1}`,
        description: "",
        type: "sequential",
        order: steps.length,
        trigger:
          steps.length === 0
            ? { type: "trial_start", conditions: {} }
            : { type: "previous_step", conditions: {} },
        actions: [],
        expanded: true,
      };
      if (
        typeof insertIndex === "number" &&
        insertIndex >= 0 &&
        insertIndex < steps.length
      ) {
        // Insert with manual reindex
        const reordered = steps
          .slice(0, insertIndex + 1)
          .concat([newStep], steps.slice(insertIndex + 1))
          .map((s, i) => ({ ...s, order: i }));
        reordered.forEach((s) => upsertStep(s));
      } else {
        upsertStep(newStep);
      }
      selectStep(newStep.id);
      onStepCreate?.(newStep);
      void recomputeHash();
    },
    [steps, upsertStep, selectStep, onStepCreate, recomputeHash],
  );

  const deleteStep = useCallback(
    (step: ExperimentStep) => {
      removeStep(step.id);
      onStepDelete?.(step.id);
      if (selectedStepId === step.id) selectStep(undefined);
      void recomputeHash();
    },
    [removeStep, onStepDelete, selectedStepId, selectStep, recomputeHash],
  );

  const toggleExpanded = useCallback(
    (step: ExperimentStep) => {
      upsertStep({ ...step, expanded: !step.expanded });
    },
    [upsertStep],
  );

  const renameStep = useCallback(
    (step: ExperimentStep, name: string) => {
      upsertStep({ ...step, name });
    },
    [upsertStep],
  );

  const deleteAction = useCallback(
    (stepId: string, actionId: string) => {
      removeAction(stepId, actionId);
      if (selectedActionId === actionId) selectAction(stepId, undefined);
      void recomputeHash();
    },
    [removeAction, selectedActionId, selectAction, recomputeHash],
  );

  const handleReorderStep = useCallback(
    (stepId: string, direction: "up" | "down") => {
      console.log("handleReorderStep", stepId, direction);
      const currentIndex = steps.findIndex((s) => s.id === stepId);
      console.log("currentIndex", currentIndex, "total", steps.length);
      if (currentIndex === -1) return;
      const newIndex = direction === "up" ? currentIndex - 1 : currentIndex + 1;
      console.log("newIndex", newIndex);
      if (newIndex < 0 || newIndex >= steps.length) return;
      reorderStep(currentIndex, newIndex);
    },
    [steps, reorderStep],
  );

  const handleReorderAction = useCallback(
    (stepId: string, actionId: string, direction: "up" | "down") => {
      const step = steps.find((s) => s.id === stepId);
      if (!step) return;

      const findInTree = (
        list: ExperimentAction[],
        pId: string | null,
      ): {
        list: ExperimentAction[];
        parentId: string | null;
        index: number;
      } | null => {
        const idx = list.findIndex((a) => a.id === actionId);
        if (idx !== -1) return { list, parentId: pId, index: idx };

        for (const a of list) {
          if (a.children) {
            const res = findInTree(a.children, a.id);
            if (res) return res;
          }
        }
        return null;
      };

      const context = findInTree(step.actions, null);
      if (!context) return;

      const { parentId, index, list } = context;
      const newIndex = direction === "up" ? index - 1 : index + 1;

      if (newIndex < 0 || newIndex >= list.length) return;

      moveAction(stepId, actionId, parentId, newIndex);
    },
    [steps, moveAction],
  );

  /* ------------------------------------------------------------------------ */
  /* Sortable (Local) DnD Monitoring                                          */
  /* ------------------------------------------------------------------------ */

  const handleLocalDragStart = useCallback((e: DragStartEvent) => {
    const id = e.active.id.toString();
    if (id.startsWith("action-")) {
      // no-op
    }
  }, []);

  const handleLocalDragEnd = useCallback(
    (e: DragEndEvent) => {
      const { active, over } = e;
      if (!over || !active) {
        return;
      }
      const activeId = active.id.toString();
      const overId = over.id.toString();

      // Step reorder is now handled globally in DesignerRoot

      // Action reorder (supports nesting)
      if (activeId.startsWith("s-act-") && overId.startsWith("s-act-")) {
        const activeData = active.data.current;
        const overData = over.data.current;

        if (
          activeData &&
          overData &&
          activeData.stepId === overData.stepId &&
          activeData.type === "action" &&
          overData.type === "action"
        ) {
          const stepId = activeData.stepId as string;
          // Fix: SortableActionChip puts 'id' directly on data, not inside 'action' property
          const activeActionId = activeData.id;
          const overActionId = overData.id;

          if (activeActionId !== overActionId) {
            const newParentId = overData.parentId as string | null;
            const newIndex = overData.sortable.index; // index within that parent's list

            moveAction(stepId, activeActionId, newParentId, newIndex);
            void recomputeHash();
          }
        }
      }
    },
    [steps, reorderStep, moveAction, recomputeHash],
  );

  /* ------------------------------------------------------------------------ */
  /* Drag Over (Live Sorting)                                                  */
  /* ------------------------------------------------------------------------ */
  const handleLocalDragOver = useCallback(
    (event: DragOverEvent) => {
      const { active, over } = event;
      if (!over) return;

      const activeId = active.id.toString();
      const overId = over.id.toString();

      // Only handle action reordering
      if (activeId.startsWith("s-act-") && overId.startsWith("s-act-")) {
        const activeData = active.data.current;
        const overData = over.data.current;

        if (
          activeData &&
          overData &&
          activeData.type === "action" &&
          overData.type === "action"
        ) {
          // Fix: Access 'id' directly from data payload
          const activeActionId = activeData.id;
          const overActionId = overData.id;

          const activeStepId = activeData.stepId;
          const overStepId = overData.stepId;
          const activeParentId = activeData.parentId;
          const overParentId = overData.parentId;

          // If moving between different lists (parents/steps), move immediately to visualize snap
          if (activeParentId !== overParentId || activeStepId !== overStepId) {
            // Determine new index
            // verification of safe move handled by store
            moveAction(
              overStepId,
              activeActionId,
              overParentId,
              overData.sortable.index,
            );
          }
        }
      }
    },
    [moveAction],
  );

  useDndMonitor({
    onDragStart: handleLocalDragStart,
    onDragOver: handleLocalDragOver,
    onDragEnd: handleLocalDragEnd,
    onDragCancel: () => {
      // no-op
    },
  });

  /* ------------------------------------------------------------------------ */
  /* Step Row (Sortable + Virtualized)                                         */
  /* ------------------------------------------------------------------------ */
  // StepRow moved outside of component to prevent re-mounting on every render (flashing fix)

  const registerMeasureRef = useCallback(
    (stepId: string, el: HTMLDivElement | null) => {
      const prev = measureRefs.current.get(stepId) ?? null;
      if (prev && prev !== el) {
        roRef.current?.unobserve(prev);
        measureRefs.current.delete(stepId);
      }
      if (el) {
        measureRefs.current.set(stepId, el);
        roRef.current?.observe(el);
      }
    },
    [],
  );

  /* ------------------------------------------------------------------------ */
  /* Render                                                                    */
  /* ------------------------------------------------------------------------ */
  return (
    <div className={cn("flex h-full min-h-0 flex-col", className)}>
      <div className="flex items-center justify-between border-b px-3 py-2 text-xs">
        <div className="flex items-center gap-3 font-medium">
          <span className="text-muted-foreground flex items-center gap-1">
            <GitBranch className="h-4 w-4" />
            Flow
          </span>
          <span className="text-muted-foreground/70">
            {steps.length} steps •{" "}
            {steps.reduce((s, st) => s + st.actions.length, 0)} actions
          </span>
        </div>
        <div className="flex items-center gap-2">
          <Button
            size="sm"
            className="h-7 px-2 text-[11px]"
            onClick={() => createStep()}
          >
            <Plus className="mr-1 h-3 w-3" />
            Step
          </Button>
        </div>
      </div>

      <div
        ref={containerRef}
        id="tour-designer-canvas"
        // Removed 'border' class to fix double border issue
        className="relative h-0 min-h-0 flex-1 overflow-x-hidden overflow-y-auto rounded-md"
        onScroll={onScroll}
      >
        {steps.length === 0 ? (
          <div className="absolute inset-0 flex items-center justify-center p-6">
            <div className="text-center">
              <div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-full border">
                <GitBranch className="text-muted-foreground h-6 w-6" />
              </div>
              <p className="mb-2 text-sm font-medium">No steps yet</p>
              <p className="text-muted-foreground mb-3 text-xs">
                Create your first step to begin designing the flow.
              </p>
              <Button
                size="sm"
                className="h-7 px-2 text-[11px]"
                onClick={() => createStep()}
              >
                <Plus className="mr-1 h-3 w-3" /> Add Step
              </Button>
            </div>
          </div>
        ) : (
          <SortableContext
            items={steps.map((s) => sortableStepId(s.id))}
            strategy={verticalListSortingStrategy}
          >
            <div style={{ height: totalHeight, position: "relative" }}>
              {virtualItems.map(
                (vi) =>
                  vi.visible && (
                    <StepRow
                      key={vi.key}
                      item={vi}
                      step={vi.step}
                      totalSteps={steps.length}
                      selectedStepId={selectedStepId}
                      selectedActionId={selectedActionId}
                      renamingStepId={renamingStepId}
                      onSelectStep={selectStep}
                      onSelectAction={selectAction}
                      onToggleExpanded={toggleExpanded}
                      onRenameStep={(step, name) => {
                        renameStep(step, name);
                        void recomputeHash();
                      }}
                      onDeleteStep={deleteStep}
                      onDeleteAction={deleteAction}
                      setRenamingStepId={setRenamingStepId}
                      registerMeasureRef={registerMeasureRef}
                      onReorderStep={handleReorderStep}
                      onReorderAction={handleReorderAction}
                      isChild={childStepIds.has(vi.step.id)}
                    />
                  ),
              )}
            </div>
          </SortableContext>
        )}
      </div>
    </div>
  );
}

// Wrap in React.memo to prevent unnecessary re-renders causing flashing
export default React.memo(FlowWorkspace);
