'use client';
import { useState, useEffect, useRef, useCallback, useLayoutEffect } from 'react';
import {
  DndContext,
  useDraggable,
  useDroppable,
  DragOverlay,
  MouseSensor,
  TouchSensor,
  useSensor,
  useSensors,
  type DragEndEvent,
} from '@dnd-kit/core';
import { CSS } from '@dnd-kit/utilities';
import { getFieldType, type SignatureFieldType } from '@/lib/db/schema';
import type { SignatureFieldData, DocumentSigner } from '@/lib/db/schema';

interface PageInfo {
  originalWidth: number;
  originalHeight: number;
  width: number;
  height: number;
  scale: number;
}

// Screen (DOM) → PDF user space. Y-axis flip required.
// DOM: Y=0 at top, increases downward
// PDF: Y=0 at bottom, increases upward
// renderedW/renderedH must be the actual rendered canvas dimensions (from pageInfo.width/height)
function screenToPdfCoords(
  screenX: number,
  screenY: number,
  renderedW: number,
  renderedH: number,
  pageInfo: PageInfo,
) {
  const pdfX = (screenX / renderedW) * pageInfo.originalWidth;
  const pdfY = ((renderedH - screenY) / renderedH) * pageInfo.originalHeight;
  return { x: pdfX, y: pdfY };
}

// PDF user space → screen (for rendering stored fields)
// renderedW/renderedH must be the actual rendered canvas dimensions (from pageInfo.width/height)
function pdfToScreenCoords(
  pdfX: number,
  pdfY: number,
  renderedW: number,
  renderedH: number,
  pageInfo: PageInfo,
) {
  const left = (pdfX / pageInfo.originalWidth) * renderedW;
  // top is measured from DOM top; pdfY is from PDF bottom — reverse flip
  const top = renderedH - (pdfY / pageInfo.originalHeight) * renderedH;
  return { left, top };
}

async function persistFields(docId: string, fields: SignatureFieldData[]) {
  try {
    await fetch(`/api/documents/${docId}/fields`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(fields),
    });
  } catch (e) {
    console.error('Failed to persist fields:', e);
  }
}

// Token color palette — each maps to a SignatureFieldType
const PALETTE_TOKENS: Array<{ id: SignatureFieldType; label: string; color: string }> = [
  { id: 'client-signature', label: 'Signature',       color: '#2563eb' },  // blue
  { id: 'initials',         label: 'Initials',        color: '#7c3aed' },  // purple
  { id: 'checkbox',         label: 'Checkbox',        color: '#059669' },  // green
  { id: 'date',             label: 'Date',            color: '#d97706' },  // amber
  { id: 'text',             label: 'Text',            color: '#64748b' },  // slate
  { id: 'agent-signature',  label: 'Agent Signature', color: '#dc2626' },  // red
  { id: 'agent-initials',   label: 'Agent Initials',  color: '#ea580c' },  // orange
];

// Draggable token in the palette
function DraggableToken({ id, label, color }: { id: string; label: string; color: string }) {
  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ id });
  const style: React.CSSProperties = {
    // No transform — DragOverlay handles the ghost. Applying transform here causes snap-back animation.
    opacity: isDragging ? 0.4 : 1,
    cursor: 'grab',
    padding: '6px 12px',
    border: `2px dashed ${color}`,
    borderRadius: '4px',
    background: `${color}14`,  // ~8% opacity tint
    color,
    fontSize: '13px',
    fontWeight: 600,
    userSelect: 'none',
    touchAction: 'none',
  };

  return (
    <div ref={setNodeRef} style={style} {...listeners} {...attributes}>
      + {label}
    </div>
  );
}

type ResizeCorner = 'se' | 'sw' | 'ne' | 'nw';

interface DraggingState {
  type: 'move' | 'resize';
  corner?: ResizeCorner;
  fieldId: string;
  startPointerX: number;
  startPointerY: number;
  startFieldX: number;  // PDF units
  startFieldY: number;
  startFieldW?: number;
  startFieldH?: number;
}

// Droppable zone wrapper for the PDF canvas
function DroppableZone({
  id,
  containerRef,
  children,
  onZonePointerMove,
  onZonePointerUp,
  onClick,
}: {
  id: string;
  containerRef: React.RefObject<HTMLDivElement | null>;
  children: React.ReactNode;
  onZonePointerMove: (e: React.PointerEvent<HTMLDivElement>) => void;
  onZonePointerUp: (e: React.PointerEvent<HTMLDivElement>) => void;
  onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
}) {
  const { setNodeRef } = useDroppable({ id });

  return (
    <div
      ref={(node) => {
        setNodeRef(node);
        if (containerRef && 'current' in containerRef) {
          (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
        }
      }}
      style={{ position: 'relative' }}
      onPointerMove={onZonePointerMove}
      onPointerUp={onZonePointerUp}
      onClick={onClick}
    >
      {children}
    </div>
  );
}

interface FieldPlacerProps {
  docId: string;
  pageInfo: PageInfo | null;
  currentPage: number;
  children: React.ReactNode;
  readOnly?: boolean;
  onFieldsChanged?: () => void;
  selectedFieldId?: string | null;
  textFillData?: Record<string, string>;
  onFieldSelect?: (fieldId: string | null) => void;
  onFieldValueChange?: (fieldId: string, value: string) => void;
  aiPlacementKey?: number;
  signers?: DocumentSigner[];
  unassignedFieldIds?: Set<string>;
}

export function FieldPlacer({ docId, pageInfo, currentPage, children, readOnly = false, onFieldsChanged, selectedFieldId, textFillData, onFieldSelect, onFieldValueChange, aiPlacementKey = 0, signers = [], unassignedFieldIds = new Set() }: FieldPlacerProps) {
  const [fields, setFields] = useState<SignatureFieldData[]>([]);
  const [isDraggingToken, setIsDraggingToken] = useState<string | null>(null);
  const [activeSignerEmail, setActiveSignerEmail] = useState<string | null>(null);
  const containerRef = useRef<HTMLDivElement | null>(null);
  // Track rendered canvas dimensions in state so renderFields re-runs when they change
  const [containerSize, setContainerSize] = useState<{ w: number; h: number } | null>(null);
  // Offset of the <canvas> element relative to the DroppableZone wrapper div
  const [canvasOffset, setCanvasOffset] = useState({ x: 0, y: 0 });

  // Track in-progress move/resize without causing re-renders
  const draggingRef = useRef<DraggingState | null>(null);
  // Track which field is actively being dragged/resized for cursor styling
  const [activeDragFieldId, setActiveDragFieldId] = useState<string | null>(null);
  const [activeDragType, setActiveDragType] = useState<'move' | 'resize' | null>(null);

  // Refs for pageInfo and fields so pointer handlers always see current values
  const pageInfoRef = useRef<PageInfo | null>(null);
  const fieldsRef = useRef<SignatureFieldData[]>([]);
  const containerSizeRef = useRef<{ w: number; h: number } | null>(null);
  const canvasOffsetRef = useRef({ x: 0, y: 0 });

  useEffect(() => { pageInfoRef.current = pageInfo; }, [pageInfo]);
  useEffect(() => { fieldsRef.current = fields; }, [fields]);
  useEffect(() => { containerSizeRef.current = containerSize; }, [containerSize]);
  useEffect(() => { canvasOffsetRef.current = canvasOffset; }, [canvasOffset]);

  // Default active signer to first signer on load; keep current if still valid (D-08)
  useEffect(() => {
    if (signers && signers.length > 0) {
      setActiveSignerEmail(prev => {
        if (prev && signers.some(s => s.email === prev)) return prev;
        return signers[0].email;
      });
    } else {
      setActiveSignerEmail(null);
    }
  }, [signers]);

  // Configure sensors: require a minimum drag distance so clicks on delete buttons
  // are not intercepted as drag starts
  const sensors = useSensors(
    useSensor(MouseSensor, { activationConstraint: { distance: 5 } }),
    useSensor(TouchSensor, { activationConstraint: { delay: 150, tolerance: 5 } }),
  );

  // Load existing fields from server on mount and whenever aiPlacementKey changes
  // aiPlacementKey increments after AI placement so this re-fetches AI-placed fields from DB
  useEffect(() => {
    async function loadFields() {
      try {
        const res = await fetch(`/api/documents/${docId}/fields`);
        if (res.ok) {
          const data = await res.json();
          setFields(data);
        }
      } catch (e) {
        console.error('Failed to load fields:', e);
      }
    }
    loadFields();
  }, [docId, aiPlacementKey]);

  // Update containerSize whenever pageInfo changes (page load or zoom change)
  // Use pageInfo.width/height (from react-pdf canvas) as the authoritative rendered size.
  // getBoundingClientRect() is only used for the drop origin (containerRect.left/top).
  useLayoutEffect(() => {
    if (!pageInfo) return;
    setContainerSize({ w: pageInfo.width, h: pageInfo.height });
  }, [pageInfo]);

  // Track the canvas element's offset within the DroppableZone wrapper div so that
  // field overlays (position: absolute relative to the wrapper) are placed correctly.
  useLayoutEffect(() => {
    if (!pageInfo || !containerRef.current) return;
    const canvas = containerRef.current.querySelector('canvas');
    if (!canvas) return;
    const canvasRect = canvas.getBoundingClientRect();
    const containerRect = containerRef.current.getBoundingClientRect();
    setCanvasOffset({
      x: canvasRect.left - containerRect.left,
      y: canvasRect.top - containerRect.top,
    });
  }, [pageInfo]);

  const handleDragEnd = useCallback(
    (event: DragEndEvent) => {
      setIsDraggingToken(null);

      if (readOnly) return;

      const { over, active } = event;
      if (!over || over.id !== 'pdf-drop-zone') return;
      if (!pageInfo) return;

      // Use the ghost's final bounding rect so the field appears exactly where
      // the ghost was — not at cursor, which is offset by the grab point.
      const ghostRect = active.rect.current.translated;
      if (!ghostRect) return;

      // Use the actual <canvas> element as the coordinate reference to avoid any
      // offset between the DroppableZone wrapper div and the inner canvas.
      const canvas = containerRef.current?.querySelector('canvas');
      const refRect = (canvas ?? containerRef.current)?.getBoundingClientRect();
      if (!refRect) return;

      const renderedW = pageInfo.width;
      const renderedH = pageInfo.height;

      // Ghost's top-left position relative to the canvas
      const rawX = ghostRect.left - refRect.left;
      const rawY = ghostRect.top - refRect.top;

      // Determine the field type from the dnd-kit active.id (token id IS the SignatureFieldType)
      const validTypes = new Set<string>(['client-signature', 'initials', 'text', 'checkbox', 'date', 'agent-signature', 'agent-initials']);
      const droppedType: SignatureFieldType = validTypes.has(active.id as string)
        ? (active.id as SignatureFieldType)
        : 'client-signature';

      // Checkbox fields are square (24x24pt). All other types: 144x36pt.
      const isCheckbox = droppedType === 'checkbox';
      const fieldW = isCheckbox ? 24 : 144;
      const fieldH = isCheckbox ? 24 : 36;

      // Field dimensions in screen pixels (for clamping)
      const fieldWpx = (fieldW / pageInfo.originalWidth) * renderedW;
      const fieldHpx = (fieldH / pageInfo.originalHeight) * renderedH;

      // Clamp so field stays within canvas bounds
      const clampedX = Math.max(0, Math.min(rawX, renderedW - fieldWpx));
      const clampedY = Math.max(0, Math.min(rawY, renderedH - fieldHpx));

      // Convert to PDF user-space coordinates.
      // pdfX = left edge (X increases right in both screen and PDF).
      // pdfY = bottom edge (PDF Y=0 is bottom, increases upward).
      const pdfX = (clampedX / renderedW) * pageInfo.originalWidth;
      const pdfY = ((renderedH - (clampedY + fieldHpx)) / renderedH) * pageInfo.originalHeight;

      const newField: SignatureFieldData = {
        id: crypto.randomUUID(),
        page: currentPage,
        x: pdfX,
        y: pdfY,
        width: fieldW,
        height: fieldH,
        type: droppedType,
        ...(activeSignerEmail ? { signerEmail: activeSignerEmail } : {}),
      };

      const next = [...fields, newField];
      setFields(next);
      persistFields(docId, next);
      onFieldsChanged?.();
    },
    [fields, pageInfo, currentPage, docId, readOnly, onFieldsChanged, activeSignerEmail],
  );

  // --- Move / Resize pointer handlers (event delegation on DroppableZone) ---

  const handleMoveStart = useCallback((e: React.PointerEvent<HTMLDivElement>, fieldId: string) => {
    if (readOnly) return;
    e.stopPropagation();
    const field = fieldsRef.current.find((f) => f.id === fieldId);
    if (!field) return;

    draggingRef.current = {
      type: 'move',
      fieldId,
      startPointerX: e.clientX,
      startPointerY: e.clientY,
      startFieldX: field.x,
      startFieldY: field.y,
    };
    setActiveDragFieldId(fieldId);
    setActiveDragType('move');
    e.currentTarget.setPointerCapture(e.pointerId);
  }, [readOnly]);

  const handleResizeStart = useCallback((e: React.PointerEvent<HTMLDivElement>, fieldId: string, corner: ResizeCorner) => {
    if (readOnly) return;
    e.stopPropagation();
    const field = fieldsRef.current.find((f) => f.id === fieldId);
    if (!field) return;

    draggingRef.current = {
      type: 'resize',
      corner,
      fieldId,
      startPointerX: e.clientX,
      startPointerY: e.clientY,
      startFieldX: field.x,
      startFieldY: field.y,
      startFieldW: field.width,
      startFieldH: field.height,
    };
    setActiveDragFieldId(fieldId);
    setActiveDragType('resize');
    e.currentTarget.setPointerCapture(e.pointerId);
  }, [readOnly]);

  const handleZonePointerMove = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
    const drag = draggingRef.current;
    if (!drag) return;

    const pi = pageInfoRef.current;
    const cs = containerSizeRef.current;
    if (!pi || !cs) return;

    const renderedW = cs.w;
    const renderedH = cs.h;

    // Screen delta from drag start
    const screenDx = e.clientX - drag.startPointerX;
    const screenDy = e.clientY - drag.startPointerY;

    // Convert screen delta to PDF units
    const pdfDx = (screenDx / renderedW) * pi.originalWidth;
    // Screen Y down = PDF Y decrease (Y-axis inversion)
    const pdfDy = -(screenDy / renderedH) * pi.originalHeight;

    if (drag.type === 'move') {
      const newX = drag.startFieldX + pdfDx;
      const newY = drag.startFieldY + pdfDy;

      // Update DOM directly for smooth performance (commit to React state on pointerup)
      const co = canvasOffsetRef.current;
      const { left, top } = pdfToScreenCoords(newX, newY, renderedW, renderedH, pi);
      const fieldEl = containerRef.current?.querySelector<HTMLElement>(`[data-field-id="${drag.fieldId}"]`);
      if (fieldEl) {
        const heightPx = parseFloat(fieldEl.style.height);
        fieldEl.style.left = `${left + co.x}px`;
        fieldEl.style.top = `${top - heightPx + co.y}px`;
      }
    } else if (drag.type === 'resize') {
      const corner = drag.corner ?? 'se';
      const startW = drag.startFieldW ?? 144;
      const startH = drag.startFieldH ?? 36;

      // Compute start rect in screen pixels
      const startLeft = (drag.startFieldX / pi.originalWidth) * renderedW;
      // pdfToScreenCoords returns top of bottom edge; field screen top = that - heightPx
      const startBottomScreenY = renderedH - (drag.startFieldY / pi.originalHeight) * renderedH;
      const startHeightPx = (startH / pi.originalHeight) * renderedH;
      const startTopScreenY = startBottomScreenY - startHeightPx;
      const startRightScreenX = startLeft + (startW / pi.originalWidth) * renderedW;

      // For each corner, the OPPOSITE corner is the anchor.
      // Compute new screen rect from pointer delta.
      let newLeft = startLeft;
      let newTop = startTopScreenY;
      let newRight = startRightScreenX;
      let newBottom = startBottomScreenY;

      if (corner === 'se') {
        // anchor: top-left; move: right and bottom
        newRight = startRightScreenX + screenDx;
        newBottom = startBottomScreenY + screenDy;
      } else if (corner === 'sw') {
        // anchor: top-right; move: left and bottom
        newLeft = startLeft + screenDx;
        newBottom = startBottomScreenY + screenDy;
      } else if (corner === 'ne') {
        // anchor: bottom-left; move: right and top
        newRight = startRightScreenX + screenDx;
        newTop = startTopScreenY + screenDy;
      } else if (corner === 'nw') {
        // anchor: bottom-right; move: left and top
        newLeft = startLeft + screenDx;
        newTop = startTopScreenY + screenDy;
      }

      // Enforce minimum screen dimensions before converting
      const MIN_W_PX = 40;
      const MIN_H_PX = 20;
      if (newRight - newLeft < MIN_W_PX) {
        if (corner === 'sw' || corner === 'nw') {
          newLeft = newRight - MIN_W_PX;
        } else {
          newRight = newLeft + MIN_W_PX;
        }
      }
      if (newBottom - newTop < MIN_H_PX) {
        if (corner === 'ne' || corner === 'nw') {
          newTop = newBottom - MIN_H_PX;
        } else {
          newBottom = newTop + MIN_H_PX;
        }
      }

      const newWidthPx = newRight - newLeft;
      const newHeightPx = newBottom - newTop;

      const fieldEl = containerRef.current?.querySelector<HTMLElement>(`[data-field-id="${drag.fieldId}"]`);
      const co = canvasOffsetRef.current;
      if (fieldEl) {
        fieldEl.style.left = `${newLeft + co.x}px`;
        fieldEl.style.top = `${newTop + co.y}px`;
        fieldEl.style.width = `${newWidthPx}px`;
        fieldEl.style.height = `${newHeightPx}px`;
      }
    }
  }, []);

  const handleZonePointerUp = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
    const drag = draggingRef.current;
    if (!drag) return;

    const pi = pageInfoRef.current;
    const cs = containerSizeRef.current;
    draggingRef.current = null;
    setActiveDragFieldId(null);
    setActiveDragType(null);

    if (!pi || !cs) return;

    const renderedW = cs.w;
    const renderedH = cs.h;

    const screenDx = e.clientX - drag.startPointerX;
    const screenDy = e.clientY - drag.startPointerY;
    const pdfDx = (screenDx / renderedW) * pi.originalWidth;
    const pdfDy = -(screenDy / renderedH) * pi.originalHeight;

    const currentFields = fieldsRef.current;

    if (drag.type === 'move') {
      const next = currentFields.map((f) => {
        if (f.id !== drag.fieldId) return f;
        return {
          ...f,
          x: drag.startFieldX + pdfDx,
          y: drag.startFieldY + pdfDy,
        };
      });
      setFields(next);
      persistFields(docId, next);
      onFieldsChanged?.();
    } else if (drag.type === 'resize') {
      const corner = drag.corner ?? 'se';
      const startW = drag.startFieldW ?? 144;
      const startH = drag.startFieldH ?? 36;

      // Compute start rect in screen pixels
      const startLeft = (drag.startFieldX / pi.originalWidth) * renderedW;
      const startBottomScreenY = renderedH - (drag.startFieldY / pi.originalHeight) * renderedH;
      const startHeightPx = (startH / pi.originalHeight) * renderedH;
      const startTopScreenY = startBottomScreenY - startHeightPx;
      const startRightScreenX = startLeft + (startW / pi.originalWidth) * renderedW;

      let newLeft = startLeft;
      let newTop = startTopScreenY;
      let newRight = startRightScreenX;
      let newBottom = startBottomScreenY;

      if (corner === 'se') {
        newRight = startRightScreenX + screenDx;
        newBottom = startBottomScreenY + screenDy;
      } else if (corner === 'sw') {
        newLeft = startLeft + screenDx;
        newBottom = startBottomScreenY + screenDy;
      } else if (corner === 'ne') {
        newRight = startRightScreenX + screenDx;
        newTop = startTopScreenY + screenDy;
      } else if (corner === 'nw') {
        newLeft = startLeft + screenDx;
        newTop = startTopScreenY + screenDy;
      }

      const MIN_W_PX = 40;
      const MIN_H_PX = 20;
      if (newRight - newLeft < MIN_W_PX) {
        if (corner === 'sw' || corner === 'nw') {
          newLeft = newRight - MIN_W_PX;
        } else {
          newRight = newLeft + MIN_W_PX;
        }
      }
      if (newBottom - newTop < MIN_H_PX) {
        if (corner === 'ne' || corner === 'nw') {
          newTop = newBottom - MIN_H_PX;
        } else {
          newBottom = newTop + MIN_H_PX;
        }
      }

      const newWidthPx = newRight - newLeft;
      const newHeightPx = newBottom - newTop;

      // Convert new screen rect back to PDF units
      // newLeft (screen) → pdfX
      const newPdfX = (newLeft / renderedW) * pi.originalWidth;
      // newBottom (screen) → pdfY (bottom edge in PDF space)
      // screen Y from top → PDF Y from bottom: pdfY = ((renderedH - screenY) / renderedH) * originalHeight
      const newPdfY = ((renderedH - newBottom) / renderedH) * pi.originalHeight;
      const newPdfW = (newWidthPx / renderedW) * pi.originalWidth;
      const newPdfH = (newHeightPx / renderedH) * pi.originalHeight;

      const next = currentFields.map((f) => {
        if (f.id !== drag.fieldId) return f;
        return { ...f, x: newPdfX, y: newPdfY, width: newPdfW, height: newPdfH };
      });
      setFields(next);
      persistFields(docId, next);
      onFieldsChanged?.();
    }
  }, [docId, onFieldsChanged]);

  // Render placed fields for the current page
  // Uses pageInfo.width/height (not getBoundingClientRect) for consistent coordinate math
  const renderFields = () => {
    if (!pageInfo || !containerSize) return null;

    const renderedW = containerSize.w;
    const renderedH = containerSize.h;

    return fields
      .filter((f) => f.page === currentPage)
      .map((field) => {
        const { left, top } = pdfToScreenCoords(field.x, field.y, renderedW, renderedH, pageInfo);
        const widthPx = (field.width / pageInfo.originalWidth) * renderedW;
        const heightPx = (field.height / pageInfo.originalHeight) * renderedH;

        const isMoving = activeDragFieldId === field.id && activeDragType === 'move';
        const isSelected = selectedFieldId === field.id;
        const currentValue = textFillData?.[field.id] ?? '';

        // Per-type color and label
        const fieldType = getFieldType(field);
        const tokenMeta = PALETTE_TOKENS.find((t) => t.id === fieldType);

        // Color override: signer color when signerEmail is set (D-06), else type color (D-07)
        let fieldColor = tokenMeta?.color ?? '#2563eb';
        if (field.signerEmail && signers) {
          const matchedSigner = signers.find(s => s.email === field.signerEmail);
          if (matchedSigner) {
            fieldColor = matchedSigner.color;
          }
        }

        // Validation overlay: unassigned field highlight (D-09/D-10)
        const isUnassigned = unassignedFieldIds?.has(field.id) ?? false;
        const fieldLabel = tokenMeta?.label ?? 'Signature';

        // Resize handle style factory
        const resizeHandle = (corner: ResizeCorner) => {
          const cursors: Record<ResizeCorner, string> = {
            nw: 'nw-resize',
            ne: 'ne-resize',
            sw: 'sw-resize',
            se: 'se-resize',
          };
          const pos: Record<ResizeCorner, React.CSSProperties> = {
            nw: { top: 0, left: 0 },
            ne: { top: 0, right: 0 },
            sw: { bottom: 0, left: 0 },
            se: { bottom: 0, right: 0 },
          };
          return (
            <div
              key={corner}
              data-no-move
              style={{
                position: 'absolute',
                ...pos[corner],
                width: 10,
                height: 10,
                cursor: cursors[corner],
                background: fieldColor,
                zIndex: 12,
                pointerEvents: readOnly ? 'none' : 'all',
              }}
              onPointerDown={(e) => {
                e.stopPropagation();
                handleResizeStart(e, field.id, corner);
              }}
            />
          );
        };

        return (
          <div
            key={field.id}
            data-field-id={field.id}
            style={{
              position: 'absolute',
              left: left + canvasOffset.x,
              top: top - heightPx + canvasOffset.y, // top is y of bottom-left corner; shift up by height
              width: widthPx,
              height: heightPx,
              border: isUnassigned ? '2px solid #ef4444' : `2px solid ${fieldColor}`,
              background: isUnassigned ? '#ef444414' : (readOnly ? `${fieldColor}0d` : `${fieldColor}1a`),
              borderRadius: '2px',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'space-between',
              padding: '0 4px',
              fontSize: '10px',
              color: fieldColor,
              // Raise above droppable overlay so the delete button is clickable
              zIndex: 10,
              pointerEvents: readOnly ? 'none' : 'all',
              boxSizing: 'border-box',
              cursor: readOnly ? 'default' : (isMoving ? 'grabbing' : (fieldType === 'text' ? 'text' : 'grab')),
              boxShadow: isSelected && fieldType === 'text'
                ? `0 0 0 2px ${fieldColor}66${isMoving ? `, 0 4px 12px ${fieldColor}59` : ''}`
                : (isMoving ? `0 4px 12px ${fieldColor}59` : undefined),
              userSelect: 'none',
              touchAction: 'none',
              opacity: readOnly ? 0.6 : 1,
            }}
            onPointerDown={(e) => {
              if (readOnly) return;
              // Only trigger move from the field body (not delete button or resize handle)
              if ((e.target as HTMLElement).closest('[data-no-move]')) return;
              handleMoveStart(e, field.id);
            }}
            onClick={(e) => {
              if (readOnly) return;
              if (getFieldType(field) === 'text') {
                e.stopPropagation(); // prevent DroppableZone's deselect handler from firing
                onFieldSelect?.(field.id);
              } else {
                // Non-text field click: deselect any selected text field
                onFieldSelect?.(null);
              }
            }}
          >
            {fieldType === 'text' ? (
              isSelected ? (
                <input
                  data-no-move
                  autoFocus
                  value={currentValue}
                  onChange={(e) => onFieldValueChange?.(field.id, e.target.value)}
                  onPointerDown={(e) => e.stopPropagation()}
                  style={{
                    flex: 1,
                    background: 'transparent',
                    border: 'none',
                    outline: 'none',
                    fontSize: '10px',
                    color: fieldColor,
                    width: '100%',
                    cursor: 'text',
                    padding: 0,
                  }}
                  placeholder="Type value..."
                />
              ) : (
                <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', pointerEvents: 'none' }}>
                  {currentValue || fieldLabel}
                </span>
              )
            ) : (
              fieldType !== 'checkbox' && (
                <span style={{ pointerEvents: 'none' }}>{fieldLabel}</span>
              )
            )}
            {!readOnly && (
              <button
                data-no-move
                onClick={(e) => {
                  // Stop dnd-kit from treating this click as a drag activation
                  e.stopPropagation();
                  const next = fields.filter((f) => f.id !== field.id);
                  setFields(next);
                  persistFields(docId, next);
                  onFieldsChanged?.();
                }}
                onPointerDown={(e) => {
                  // Prevent dnd-kit sensors and move handler from capturing this pointer event
                  e.stopPropagation();
                }}
                style={{
                  cursor: 'pointer',
                  background: 'none',
                  border: 'none',
                  color: '#ef4444',
                  fontWeight: 'bold',
                  padding: '0 2px',
                  fontSize: '12px',
                  lineHeight: 1,
                  // Ensure the button sits above any overlay that might capture events
                  position: 'relative',
                  zIndex: 11,
                  pointerEvents: 'all',
                }}
                aria-label="Remove field"
              >
                ×
              </button>
            )}
            {!readOnly && fieldType !== 'checkbox' && (
              <>
                {resizeHandle('nw')}
                {resizeHandle('ne')}
                {resizeHandle('sw')}
                {resizeHandle('se')}
              </>
            )}
          </div>
        );
      });
  };

  return (
    <DndContext
      sensors={sensors}
      onDragStart={(event) => setIsDraggingToken(event.active.id as string)}
      onDragEnd={handleDragEnd}
    >
      {/* Active signer selector — only when signers configured and not read-only (D-05) */}
      {!readOnly && signers && signers.length > 0 && (
        <div style={{
          display: 'flex',
          alignItems: 'center',
          gap: '8px',
          marginBottom: '8px',
          padding: '8px 12px',
          background: '#f8fafc',
          border: '1px solid #e2e8f0',
          borderRadius: '6px',
        }}>
          <span style={{ fontSize: '12px', color: '#6b7280', fontWeight: 500, whiteSpace: 'nowrap' }}>
            Active signer:
          </span>
          <select
            value={activeSignerEmail ?? ''}
            onChange={(e) => setActiveSignerEmail(e.target.value)}
            style={{
              flex: 1,
              height: '32px',
              border: '1px solid #D1D5DB',
              borderRadius: '0.375rem',
              fontSize: '0.875rem',
              padding: '0 8px',
              background: 'white',
            }}
          >
            {signers.map(s => (
              <option key={s.email} value={s.email}>
                {s.email}
              </option>
            ))}
          </select>
          {/* Color indicator dot next to the dropdown */}
          {(() => {
            const activeSigner = signers.find(s => s.email === activeSignerEmail);
            return activeSigner ? (
              <span style={{
                width: '8px', height: '8px', borderRadius: '50%',
                backgroundColor: activeSigner.color, flexShrink: 0,
              }} />
            ) : null;
          })()}
        </div>
      )}

      {/* Palette — hidden in read-only mode */}
      {!readOnly && (
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
            gap: '8px',
            marginBottom: '8px',
            padding: '8px 12px',
            background: '#f8fafc',
            border: '1px solid #e2e8f0',
            borderRadius: '6px',
          }}
        >
          <span style={{ fontSize: '12px', color: '#64748b', fontWeight: 500 }}>Field Palette:</span>
          {PALETTE_TOKENS.map((token) => (
            <DraggableToken key={token.id} id={token.id} label={token.label} color={token.color} />
          ))}
        </div>
      )}

      {/* Droppable PDF zone with field overlays */}
      <DroppableZone
        id="pdf-drop-zone"
        containerRef={containerRef}
        onZonePointerMove={handleZonePointerMove}
        onZonePointerUp={handleZonePointerUp}
        onClick={(e) => {
          // Deselect if clicking the zone background (not a field box)
          if (!(e.target as HTMLElement).closest('[data-field-id]')) {
            onFieldSelect?.(null);
          }
        }}
      >
        {children}
        {renderFields()}
      </DroppableZone>

      {/* Ghost overlay during drag */}
      <DragOverlay dropAnimation={null}>
        {isDraggingToken ? (() => {
          const tokenMeta = PALETTE_TOKENS.find((t) => t.id === isDraggingToken);
          const label = tokenMeta?.label ?? 'Field';
          // Ghost uses active signer color if one is selected, else type color (D-06)
          let ghostColor = tokenMeta?.color ?? '#2563eb';
          if (activeSignerEmail && signers) {
            const activeSigner = signers.find(s => s.email === activeSignerEmail);
            if (activeSigner) ghostColor = activeSigner.color;
          }
          const isCheckbox = isDraggingToken === 'checkbox';
          return (
            <div style={{
              width: isCheckbox ? 24 : 144,
              height: isCheckbox ? 24 : 36,
              border: `2px solid ${ghostColor}`,
              background: `${ghostColor}26`,
              borderRadius: '2px',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              fontSize: '11px',
              color: ghostColor,
              fontWeight: 600,
              pointerEvents: 'none',
            }}>
              {!isCheckbox && label}
            </div>
          );
        })() : null}
      </DragOverlay>
    </DndContext>
  );
}
