import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
import { useEditorStore } from '../../store/editorStore';
import { useCollaborationStore } from '../../store/collaborationStore';
import { VisualElement } from '../../types/blocks';
import PropertyPanel from './PropertyPanel';
import ElementTree from './ElementTree';
import Toolbox from './Toolbox';
import {
  Monitor, Tablet, Smartphone, Grid3X3,
  Eye, EyeOff, Trash2, Copy,
  Plus, Code, Palette, Layout
} from 'lucide-react';
import { getColor } from '../Collaboration/CollaboratorAvatars';
import { getSocket } from '../../hooks/useWebSocket';

type DeviceMode = 'desktop' | 'tablet' | 'mobile';
type DropPosition = 'before' | 'after' | 'inside' | null;

const DEVICE_SIZES: Record<DeviceMode, { width: number; height: number }> = {
  desktop: { width: 1280, height: 800 },
  tablet: { width: 768, height: 1024 },
  mobile: { width: 375, height: 667 },
};

function uuid() { return crypto.randomUUID?.() || Math.random().toString(36).slice(2, 11); }

function createVisualElement(tagName: string, defaultProps: any): VisualElement {
  const id = uuid();
  return {
    id, tagName, type: 'element',
    children: defaultProps?.children || [],
    props: Object.fromEntries(Object.entries(defaultProps || {}).filter(([k]) => k !== 'children' && k !== 'style')),
    styles: defaultProps?.style || {},
    classes: [], attributes: {},
  };
}

function duplicateElement(el: VisualElement): VisualElement {
  return { ...el, id: uuid(), children: (el.children || []).map(duplicateElement) };
}

// Tree helpers
function findElementById(elements: VisualElement[] | undefined, id: string): VisualElement | null {
  if (!elements) return null;
  for (const el of elements) {
    if (el.id === id) return el;
    if (el.children) { const f = findElementById(el.children, id); if (f) return f; }
  }
  return null;
}

function removeFromTree(elements: VisualElement[], id: string): { result: VisualElement[]; removed: VisualElement | null } {
  let removed: VisualElement | null = null;
  const filter = (list: VisualElement[]): VisualElement[] =>
    list.filter(el => {
      if (el.id === id) { removed = el; return false; }
      if (el.children) el.children = filter(el.children);
      return true;
    });
  return { result: filter([...elements]), removed };
}

function insertAt(elements: VisualElement[], targetId: string, newEl: VisualElement, pos: DropPosition): VisualElement[] {
  if (pos === 'inside') {
    return elements.map(el => {
      if (el.id === targetId) return { ...el, children: [...(el.children || []), newEl] };
      if (el.children) return { ...el, children: insertAt(el.children, targetId, newEl, pos) };
      return el;
    });
  }
  const idx = elements.findIndex(el => el.id === targetId);
  if (idx === -1) {
    return elements.map(el => {
      if (el.children) return { ...el, children: insertAt(el.children, targetId, newEl, pos) };
      return el;
    });
  }
  const arr = [...elements];
  arr.splice(pos === 'before' ? idx : idx + 1, 0, newEl);
  return arr;
}

export default function VisualEditor() {
  const {
    visualElements, setVisualElements, selectedElementId, setSelectedElement,
    viewMode, setViewMode, snapToGrid, toggleSnapToGrid,
    addVisualElement, updateVisualElement, removeVisualElement,
    syncVisualElementsToRegistry, activeFileId,
  } = useEditorStore();
  const [deviceMode, setDeviceMode] = useState<DeviceMode>('desktop');
  const [showGuides, setShowGuides] = useState(true);
  const [contextMenu, setContextMenu] = useState<{ x: number; y: number; elementId: string } | null>(null);
  const [colorPicker, setColorPicker] = useState<{ elementId: string; property: string; x: number; y: number } | null>(null);
  const [showTagModal, setShowTagModal] = useState(false);
  const [dragOverId, setDragOverId] = useState<string | null>(null);
  const [dragOverPos, setDragOverPos] = useState<DropPosition>(null);
  const [draggedId, setDraggedId] = useState<string | null>(null);
  const canvasRef = useRef<HTMLDivElement>(null);

  // Set up global WS hover emit (throttled to prevent flooding)
  useEffect(() => {
    let lastHoverEmit = 0;
    let lastHoverId: string | null = null;
    (window as any).__wsElementHoverEmit = (elementId: string | null) => {
      const now = Date.now();
      if (elementId === lastHoverId && now - lastHoverEmit < 60) return;
      lastHoverEmit = now;
      lastHoverId = elementId;
      const socket = getSocket();
      const projectId = (window as any).__projectId;
      if (socket?.connected && projectId) {
        socket.emit('visual_element_hover', { projectId, elementId });
      }
    };
    (window as any).__wsElementHover = (elementId: string | null) => {
      const fn = (window as any).__wsElementHoverEmit;
      if (fn) fn(elementId);
    };
    return () => {
      (window as any).__wsElementHover = undefined;
      (window as any).__wsElementHoverEmit = undefined;
    };
  }, []);

  // Collaborator element hovers for overlay
  const collaborators = useCollaborationStore((s) => s.collaborators);
  const collaboratorHoverMap = useMemo(() => {
    const map = new Map<string, { userId: string; username: string; color: string }[]>();
    collaborators.forEach((c) => {
      if (c.selectedElementId) {
        const arr = map.get(c.selectedElementId) || [];
        arr.push({ userId: c.userId, username: c.username, color: getColor(c.userId) });
        map.set(c.selectedElementId, arr);
      }
    });
    // Set global for child renderers
    (window as any).__wsCollaboratorHoverMap = map;
    return map;
  }, [collaborators]);
  const cmRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handler = (e: MouseEvent) => {
      if (cmRef.current && !cmRef.current.contains(e.target as Node)) setContextMenu(null);
    };
    if (contextMenu) document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [contextMenu]);

  // Combined drop handler: toolbox elements and reorder
  const handleCanvasDrop = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    const toolboxData = e.dataTransfer.getData('application/visual-element');
    if (toolboxData) {
      const preset = JSON.parse(toolboxData);
      const element = createVisualElement(preset.tagName, preset.defaultProps);
      const target = (e.target as HTMLElement).closest('[data-element-id]');
      const parentId = target?.getAttribute('data-element-id') || undefined;
      addVisualElement(element, parentId);
      return;
    }
    // Reorder
    const dragId = draggedId;
    const targetId = dragOverId;
    setDraggedId(null);
    setDragOverId(null);
    setDragOverPos(null);
    if (!dragId || !targetId || dragId === targetId) return;
    const { result: afterRemove, removed } = removeFromTree(visualElements || [], dragId);
    if (!removed) { setVisualElements(afterRemove); if (activeFileId) syncVisualElementsToRegistry(activeFileId); return; }
    const newTree = insertAt(afterRemove, targetId, removed, dragOverPos);
    setVisualElements(newTree);
    if (activeFileId) syncVisualElementsToRegistry(activeFileId);
  }, [draggedId, dragOverId, dragOverPos, visualElements, setVisualElements, addVisualElement, syncVisualElementsToRegistry, activeFileId]);

  const handleElementDragStart = useCallback((e: React.DragEvent, elementId: string) => {
    e.stopPropagation();
    setDraggedId(elementId);
    e.dataTransfer.setData('text/plain', elementId);
    e.dataTransfer.effectAllowed = 'move';
    if (e.currentTarget instanceof HTMLElement) {
      const rect = e.currentTarget.getBoundingClientRect();
      e.dataTransfer.setDragImage(e.currentTarget, e.clientX - rect.left, e.clientY - rect.top);
    }
  }, []);

  const handleDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    const targetEl = (e.target as HTMLElement).closest('[data-element-id]');
    const targetId = targetEl?.getAttribute('data-element-id') || null;
    if (!targetId || targetId === draggedId) { setDragOverId(null); setDragOverPos(null); return; }
    setDragOverId(targetId);
    if (targetEl) {
      const rect = targetEl.getBoundingClientRect();
      const y = e.clientY - rect.top;
      const h = rect.height;
      const isContainer = !['INPUT','IMG','BR','HR'].includes(targetEl.tagName);
      if (y < h * 0.25) setDragOverPos('before');
      else if (y > h * 0.75) setDragOverPos('after');
      else if (isContainer) setDragOverPos('inside');
      else setDragOverPos('after');
    }
  }, [draggedId]);

  const handleDragEnd = useCallback(() => {
    setDraggedId(null); setDragOverId(null); setDragOverPos(null);
  }, []);

  const handleElementClick = useCallback((e: React.MouseEvent, elementId: string) => {
    e.stopPropagation();
    setContextMenu(null); setColorPicker(null);
    setSelectedElement(elementId);
  }, [setSelectedElement]);

  const handleCanvasClick = useCallback(() => {
    setSelectedElement(null); setContextMenu(null); setColorPicker(null);
  }, [setSelectedElement]);

  const handleContextMenu = useCallback((e: React.MouseEvent, elementId: string) => {
    e.preventDefault(); e.stopPropagation();
    setSelectedElement(elementId);
    setContextMenu({ x: e.clientX, y: e.clientY, elementId });
  }, [setSelectedElement]);

  const selectedElement = selectedElementId ? findElementById(visualElements || [], selectedElementId) : null;
  const device = DEVICE_SIZES[deviceMode];

  // Context menu actions
  const cmDuplicate = useCallback(() => {
    if (!contextMenu) return;
    const el = findElementById(visualElements || [], contextMenu.elementId);
    if (el) addVisualElement(duplicateElement(el));
    setContextMenu(null);
  }, [contextMenu, visualElements, addVisualElement]);

  const cmDelete = useCallback(() => {
    if (contextMenu) { removeVisualElement(contextMenu.elementId); setContextMenu(null); }
  }, [contextMenu, removeVisualElement]);

  const cmChangeTag = useCallback(() => { setShowTagModal(true); }, []);

  const handleTagChange = useCallback((newTag: string) => {
    if (!contextMenu) return;
    updateVisualElement(contextMenu.elementId, { tagName: newTag });
    setShowTagModal(false); setContextMenu(null);
  }, [contextMenu, updateVisualElement]);

  const cmAddChild = useCallback(() => {
    if (!contextMenu) return;
    const child = createVisualElement('div', { style: { padding: '12px', border: '1px dashed #94a3b8', borderRadius: '4px' } });
    addVisualElement(child, contextMenu.elementId);
    setContextMenu(null);
  }, [contextMenu, addVisualElement]);

  const handleAddChild = useCallback((parentId: string) => {
    addVisualElement(createVisualElement('div', { style: { padding: '12px', border: '1px dashed #6366f1', borderRadius: '4px' } }), parentId);
  }, [addVisualElement]);

  const cmColorPick = useCallback(() => {
    if (!contextMenu) return;
    setColorPicker({ elementId: contextMenu.elementId, property: 'color', x: contextMenu.x, y: contextMenu.y });
    setContextMenu(null);
  }, [contextMenu]);

  const cmBgColorPick = useCallback(() => {
    if (!contextMenu) return;
    setColorPicker({ elementId: contextMenu.elementId, property: 'backgroundColor', x: contextMenu.x, y: contextMenu.y });
    setContextMenu(null);
  }, [contextMenu]);

  const ALL_TAGS = ['div','span','p','h1','h2','h3','h4','h5','h6','a','button','input','img','form','section','header','footer','nav','main','aside','ul','ol','li','table','label','textarea','select','article','blockquote','pre','code','figure','figcaption','details','summary','dialog','menu','strong','em','b','i','u','s','mark','small','sub','sup','cite','time','address','video','audio','canvas','iframe','fieldset','legend','progress','meter','dl','dt','dd'];

  function TagModal({ currentTag }: { currentTag: string }) {
    const [search, setSearch] = useState('');
    const filtered = search ? ALL_TAGS.filter(t => t.includes(search.toLowerCase())) : ALL_TAGS;
    return (
      <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/30" onClick={() => { setShowTagModal(false); setContextMenu(null); }}>
        <div className="bg-surface-900 border border-surface-700 rounded-lg shadow-xl p-4 w-64" onClick={e => e.stopPropagation()}>
          <h3 className="text-xs font-semibold text-surface-200 mb-2">Change Tag</h3>
          <input type="text" value={search} onChange={e => setSearch(e.target.value)}
            className="w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 text-xs text-surface-200 mb-2" placeholder="Search tags..." autoFocus />
          <div className="max-h-48 overflow-y-auto space-y-0.5">
            {filtered.map(tag => (
              <button key={tag} onClick={() => handleTagChange(tag)}
                className={`w-full text-left px-2 py-1 rounded text-xs transition-colors ${tag === currentTag ? 'bg-primary-500/20 text-primary-300' : 'text-surface-300 hover:bg-surface-700'}`}>
                &lt;{tag}&gt;
              </button>
            ))}
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="flex h-full">
      {/* Toolbox */}
      <div className="w-56 panel border-r border-surface-700 flex flex-col py-2 overflow-y-auto">
        <div className="px-3 pb-2 text-xs font-semibold text-surface-400 uppercase tracking-wider border-b border-surface-700 mb-2">
          Elements
        </div>
        <div className="flex-1 overflow-y-auto px-2">
          <Toolbox />
        </div>
      </div>

      {/* Canvas */}
      <div className="flex-1 flex flex-col bg-surface-950/50">
        <div className="h-10 glass border-b border-surface-700 flex items-center justify-between px-4">
          <div className="flex items-center gap-2">
            <div className="flex bg-surface-800 rounded-lg p-0.5 gap-0.5">
              {(['desktop','tablet','mobile'] as DeviceMode[]).map(m => {
                const Ic = m === 'desktop' ? Monitor : m === 'tablet' ? Tablet : Smartphone;
                return (
                  <button key={m} onClick={() => setDeviceMode(m)}
                    className={`p-1.5 rounded-md transition-all ${deviceMode === m ? 'bg-primary-600 text-white' : 'text-surface-400 hover:text-white'}`} title={m}>
                    <Ic className="w-3.5 h-3.5" />
                  </button>
                );
              })}
            </div>
            <div className="w-px h-4 bg-surface-700" />
            <button onClick={toggleSnapToGrid}
              className={`p-1.5 rounded-md ${snapToGrid ? 'text-primary-400' : 'text-surface-400 hover:text-white'}`} title="Snap to Grid">
              <Grid3X3 className="w-3.5 h-3.5" />
            </button>
            <button onClick={() => setShowGuides(!showGuides)}
              className={`p-1.5 rounded-md ${showGuides ? 'text-primary-400' : 'text-surface-400 hover:text-white'}`} title="Guides">
              {showGuides ? <Eye className="w-3.5 h-3.5" /> : <EyeOff className="w-3.5 h-3.5" />}
            </button>
          </div>
          <div className="flex items-center gap-2">
            <span className="text-xs text-surface-500">{(visualElements || []).length} elements</span>
          </div>
        </div>

        <div className="flex-1 overflow-auto p-8 flex items-start justify-center bg-surface-950"
          style={{
            backgroundImage: snapToGrid ? 'radial-gradient(circle, rgba(148,163,184,0.1) 1px, transparent 1px)' : 'none',
            backgroundSize: snapToGrid ? '20px 20px' : 'auto',
          }}>
          <div ref={canvasRef}
            className="bg-white rounded-lg shadow-2xl transition-all duration-300 overflow-hidden relative"
            style={{ width: `${device.width}px`, minHeight: `${device.height}px`, maxWidth: '100%' }}
            onDrop={handleCanvasDrop} onDragOver={handleDragOver}
            onClick={handleCanvasClick}>
            {(visualElements || []).length === 0 ? (
              <div className="flex items-center justify-center h-full min-h-[400px]" style={{ color: '#94a3b8', fontSize: '14px' }}>
                <div className="text-center">
                  <Layout className="w-12 h-12 mx-auto mb-3 opacity-30" />
                  <p>Drag elements from the toolbox</p>
                  <p className="text-xs mt-1">or drag existing elements to reorder them</p>
                </div>
              </div>
            ) : (
              <div className="p-4">
                {(visualElements || []).map((el) => (
                  <VisualElementRenderer key={el.id} element={el}
                    selectedId={selectedElementId} onSelect={handleElementClick}
                    onContextMenu={handleContextMenu} depth={0}
                    dragOverId={dragOverPos ? dragOverId : null}
                    dragOverPos={dragOverPos}
                    onAddChild={handleAddChild}
                    onDragStart={handleElementDragStart}
                    onDrop={handleCanvasDrop}
                    onDragOver={handleDragOver}
                    onDragEnd={handleDragEnd}
                    draggedId={draggedId}
                    collaboratorHovers={collaboratorHoverMap.get(el.id) || []} />
                ))}
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Sidebar */}
      <div className="w-80 panel border-l border-surface-700 overflow-y-auto">
        <div className="p-3 border-b border-surface-700">
          <div className="flex gap-1 bg-surface-800 rounded-lg p-0.5">
            <button onClick={() => setViewMode('design')}
              className={`flex-1 px-3 py-1.5 rounded-md text-xs font-medium transition-all ${viewMode === 'design' ? 'bg-primary-600 text-white' : 'text-surface-400'}`}>Properties</button>
            <button onClick={() => setViewMode('preview')}
              className={`flex-1 px-3 py-1.5 rounded-md text-xs font-medium transition-all ${viewMode === 'preview' ? 'bg-primary-600 text-white' : 'text-surface-400'}`}>Tree</button>
          </div>
        </div>
        {viewMode === 'design' ? (
          <PropertyPanel element={selectedElement} />
        ) : (
          <ElementTree elements={visualElements || []} selectedId={selectedElementId}
            onSelect={setSelectedElement} onContextMenu={handleContextMenu} />
        )}
      </div>

      {/* Context Menu */}
      {contextMenu && (
        <div ref={cmRef}
          className="fixed z-50 bg-surface-900 border border-surface-700 rounded-lg shadow-xl py-1 min-w-[160px]"
          style={{ left: contextMenu.x, top: contextMenu.y }}>
          <button onClick={cmDuplicate} className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-surface-300 hover:bg-surface-700"><Copy className="w-3.5 h-3.5" /> Duplicate</button>
          <button onClick={cmDelete} className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-red-400 hover:bg-surface-700"><Trash2 className="w-3.5 h-3.5" /> Delete</button>
          <div className="h-px bg-surface-700 my-1" />
          <button onClick={cmChangeTag} className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-surface-300 hover:bg-surface-700"><Code className="w-3.5 h-3.5" /> Change Tag</button>
          <button onClick={cmAddChild} className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-surface-300 hover:bg-surface-700"><Plus className="w-3.5 h-3.5" /> Add Child</button>
          <div className="h-px bg-surface-700 my-1" />
          <button onClick={cmColorPick} className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-surface-300 hover:bg-surface-700"><Palette className="w-3.5 h-3.5" /> Text Color</button>
          <button onClick={cmBgColorPick} className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-surface-300 hover:bg-surface-700"><Palette className="w-3.5 h-3.5" /> Background Color</button>
        </div>
      )}

      {/* Color Picker */}
      {colorPicker && (
        <div className="fixed z-50" style={{ left: colorPicker.x, top: colorPicker.y }}>
          <input type="color" value="#6366f1" autoFocus
            onChange={(e) => {
              updateVisualElement(colorPicker.elementId, { styles: { ...(findElementById(visualElements || [], colorPicker.elementId)?.styles || {}), [colorPicker.property]: e.target.value } });
              setColorPicker(null);
            }}
            className="w-32 h-32 cursor-pointer border-0 rounded-lg shadow-xl"
            style={{ background: 'none' }} onBlur={() => setColorPicker(null)} />
        </div>
      )}

      {showTagModal && contextMenu && (
        <TagModal currentTag={findElementById(visualElements || [], contextMenu.elementId)?.tagName || 'div'} />
      )}
    </div>
  );
}

// ===== VISUAL ELEMENT RENDERER =====
function VisualElementRenderer({
  element, selectedId, onSelect, onContextMenu, depth, dragOverId, dragOverPos,
  onAddChild, onDragStart, onDrop, onDragOver, onDragEnd, draggedId,
  collaboratorHovers,
}: {
  element: VisualElement; selectedId: string | null;
  onSelect: (e: React.MouseEvent, id: string) => void;
  onContextMenu: (e: React.MouseEvent, id: string) => void;
  depth: number; dragOverId: string | null; dragOverPos: DropPosition | null;
  onAddChild: (id: string) => void;
  onDragStart: (e: React.DragEvent, id: string) => void;
  onDrop: (e: React.DragEvent) => void;
  onDragOver: (e: React.DragEvent) => void;
  onDragEnd: (e: React.DragEvent) => void;
  draggedId: string | null;
  collaboratorHovers: { userId: string; username: string; color: string }[];
}) {
  const isSelected = selectedId === element.id;
  const isDragTarget = dragOverId === element.id;
  const isDragged = draggedId === element.id;
  const Tag = element.tagName as keyof JSX.IntrinsicElements;

  // Build border/indicator for drop position
  let dropIndicator: React.CSSProperties = {};
  if (isDragTarget && dragOverPos === 'before') {
    dropIndicator = { borderTop: '3px solid #6366f1' };
  } else if (isDragTarget && dragOverPos === 'after') {
    dropIndicator = { borderBottom: '3px solid #6366f1' };
  } else if (isDragTarget && dragOverPos === 'inside') {
    dropIndicator = { outline: '3px dashed #6366f1', outlineOffset: '2px' };
  }

  const style: React.CSSProperties = {
    ...element.styles,
    ...dropIndicator,
    outline: isSelected && !isDragTarget ? '2px solid #6366f1' : isDragTarget && dragOverPos === 'inside' ? '3px dashed #6366f1' : collaboratorHovers.length > 0 ? `2px solid ${collaboratorHovers[0].color}` : 'none',
    outlineOffset: isSelected || collaboratorHovers.length > 0 ? '2px' : '0',
    position: 'relative',
    cursor: isDragged ? 'grabbing' : 'grab',
    transition: 'outline 0.1s ease, border 0.1s ease',
    opacity: isDragged ? 0.5 : 1,
  };

  const attribs: Record<string, string> = { ...element.attributes, 'data-element-id': element.id };
  if (element.props?.src) attribs.src = element.props.src;
  if (element.props?.href) attribs.href = element.props.href;
  if (element.props?.alt) attribs.alt = element.props.alt;
  if (element.props?.placeholder) attribs.placeholder = element.props.placeholder;
  if (element.props?.type) attribs.type = element.props.type;
  if (element.props?.value) attribs.value = element.props.value;
  if (element.props?.name) attribs.name = element.props.name;
  if (element.props?.id) attribs.id = element.props.id;

  const isVoidTag = ['br','hr','img','input','link','meta','area','base','col','embed','source','track','wbr'].includes(element.tagName);

  if (isVoidTag) {
    return (
      <div style={{ position: 'relative', display: 'inline-block' }}>
        <Tag {...attribs} style={style} className={element.classes.join(' ')}
          draggable
          onDragStart={(e: React.DragEvent) => onDragStart(e, element.id)}
          onDragOver={onDragOver}
          onDrop={onDrop}
          onDragEnd={onDragEnd}
          onClick={(e: React.MouseEvent) => onSelect(e, element.id)}
          onContextMenu={(e: React.MouseEvent) => onContextMenu(e, element.id)}
          onMouseEnter={() => (window as any).__wsElementHover?.(element.id)}
          onMouseLeave={() => (window as any).__wsElementHover?.(null)} />
        {collaboratorHovers.length > 0 && (
          <div className="absolute top-0 right-0 flex -space-x-1">
            {collaboratorHovers.map((c) => (
              <div key={c.userId} className="w-3.5 h-3.5 rounded-full flex items-center justify-center text-[6px] font-bold text-white"
                style={{ backgroundColor: c.color }} title={c.username}>
                {c.username.charAt(0).toUpperCase()}
              </div>
            ))}
          </div>
        )}
      </div>
    );
  }

  return (
    <div style={{ position: 'relative' }}>
      <Tag {...attribs} style={style} className={element.classes.join(' ')}
        draggable
        onDragStart={(e: React.DragEvent) => onDragStart(e, element.id)}
        onDragOver={onDragOver}

        onDrop={onDrop}
        onDragEnd={onDragEnd}
        onClick={(e: React.MouseEvent) => onSelect(e, element.id)}
        onContextMenu={(e: React.MouseEvent) => onContextMenu(e, element.id)}
        onMouseEnter={() => (window as any).__wsElementHover?.(element.id)}
        onMouseLeave={() => (window as any).__wsElementHover?.(null)}>
        {collaboratorHovers.length > 0 && (
          <div className="absolute top-0 right-0 flex -space-x-1 z-10">
            {collaboratorHovers.map((c) => (
              <div key={c.userId} className="w-3.5 h-3.5 rounded-full flex items-center justify-center text-[6px] font-bold text-white"
                style={{ backgroundColor: c.color }} title={c.username}>
                {c.username.charAt(0).toUpperCase()}
              </div>
            ))}
          </div>
        )}
        {element.props?.textContent || ''}
        {(element.children || []).map((child) => (
          <VisualElementRenderer key={child.id} element={child}
            selectedId={selectedId} onSelect={onSelect} onContextMenu={onContextMenu}
            depth={depth + 1} dragOverId={dragOverId} dragOverPos={dragOverPos}
            onAddChild={onAddChild} onDragStart={onDragStart}
            onDrop={onDrop} onDragOver={onDragOver}
            onDragEnd={onDragEnd} draggedId={draggedId}
            collaboratorHovers={(window as any).__wsCollaboratorHoverMap?.get(child.id) || []} />
        ))}
      </Tag>
      {isVoidTag ? null : (
        <button onClick={(e) => { e.stopPropagation(); onAddChild(element.id); }}
          className="absolute -bottom-3 left-1/2 -translate-x-1/2 w-6 h-6 flex items-center justify-center rounded-full
            bg-primary-600 text-white shadow-lg hover:bg-primary-500 transition-all opacity-0 hover:opacity-100 z-10"
          title="Add child element">
          <Plus className="w-3.5 h-3.5" />
        </button>
      )}
    </div>
  );
}
