
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { WorkflowData, PlotNode, PlotConnection, PlotNodeType, Entity, EntityType } from '../types';
import { Plus, Trash2, ArrowRight, BookOpen, MessageCircle, Zap, Palette, Save, Link2 } from 'lucide-react';

interface StoryWorkflowProps {
  data: WorkflowData;
  onUpdate: (data: WorkflowData) => void;
  entities: Entity[];
  onNavigateToEntity: (entityId: string) => void;
}

const CARD_WIDTH = 260;
const CARD_HEIGHT = 220;

const INITIAL_COLORS = [
  '#ffffff', // White
  '#dbeafe', // Blue
  '#dcfce7', // Green
  '#fef9c3', // Yellow
  '#fee2e2', // Red
  '#f3e8ff', // Purple
];

const renderTextWithLinks = (text: string, entities: Entity[], onNavigate: (id: string) => void) => {
    if (!text) return <span className="text-slate-400 italic">Description...</span>;
    
    const parts: (string | React.ReactNode)[] = [text];
    
    entities.forEach(entity => {
        if (!entity.name) return;
        const regex = new RegExp(`(${entity.name})`, 'gi');
        
        for (let i = 0; i < parts.length; i++) {
            const part = parts[i];
            if (typeof part === 'string') {
                const split = part.split(regex);
                if (split.length > 1) {
                    const newParts = split.map((s, idx) => {
                        if (s.toLowerCase() === entity.name.toLowerCase()) {
                            return (
                                <span 
                                  key={`${entity.id}-${idx}`}
                                  onClick={(e) => { e.stopPropagation(); onNavigate(entity.id); }}
                                  className="text-indigo-600 hover:text-indigo-800 underline decoration-indigo-300 hover:decoration-indigo-600 cursor-pointer font-medium bg-indigo-50 px-0.5 rounded transition-all"
                                  title={`Voir la fiche de ${entity.name}`}
                                >
                                    {s}
                                </span>
                            );
                        }
                        return s;
                    });
                    parts.splice(i, 1, ...newParts);
                    i += newParts.length - 1;
                }
            }
        }
    });

    return <>{parts}</>;
};

interface StoryNodeProps {
  node: PlotNode;
  isSelected: boolean;
  isEditing: boolean;
  activeColorPickerId: string | null;
  entities: Entity[];
  savedColors: string[];
  
  onMouseDown: (e: React.MouseEvent, id: string) => void;
  onMouseUp: (e: React.MouseEvent, id: string) => void;
  onStartConnection: (e: React.MouseEvent, id: string) => void;
  onUpdate: (id: string, updates: Partial<PlotNode>) => void;
  onSetEditing: (id: string | null) => void;
  onToggleColorPicker: (id: string) => void;
  onSaveColor: (color: string) => void;
  onNavigateToEntity: (id: string) => void;
  
  onInputFocus: (e: React.FocusEvent) => void;
  onInputCheckAutocomplete: (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>, id: string, field: 'title'|'description') => void;
  onKeyDownInInput: (e: React.KeyboardEvent, id: string) => void;
}

const StoryNode = React.memo(({ 
    node, isSelected, isEditing, activeColorPickerId, entities, savedColors,
    onMouseDown, onMouseUp, onStartConnection, onUpdate, onSetEditing, 
    onToggleColorPicker, onSaveColor, onNavigateToEntity, 
    onInputFocus, onInputCheckAutocomplete, onKeyDownInInput
}: StoryNodeProps) => {

    const [showTypePicker, setShowTypePicker] = useState(false);

    const richDescription = useMemo(() => {
        return renderTextWithLinks(node.description, entities, onNavigateToEntity);
    }, [node.description, entities, onNavigateToEntity]);

    return (
        <div
            className={`absolute flex flex-col rounded-xl shadow-sm border transition-all z-10 group
                ${isSelected ? 'ring-2 ring-indigo-500 shadow-lg scale-[1.01]' : 'border-slate-200 hover:shadow-md'}
            `}
            style={{ 
                transform: `translate3d(${node.x}px, ${node.y}px, 0)`,
                width: CARD_WIDTH, 
                height: CARD_HEIGHT,
                backgroundColor: node.color || '#ffffff',
                willChange: 'transform'
            }}
            onMouseDown={(e) => onMouseDown(e, node.id)}
            onMouseUp={(e) => onMouseUp(e, node.id)}
            onMouseLeave={() => setShowTypePicker(false)}
        >
            <div className="h-1.5 rounded-t-xl bg-black/5 w-full cursor-grab active:cursor-grabbing" />

            <div className="flex-1 px-4 pb-4 pt-2 flex flex-col overflow-hidden relative">
                <div className="flex justify-between items-start mb-2 relative">
                    {isEditing ? (
                        <input
                            className="font-bold text-slate-800 bg-white/50 border-b border-indigo-400 outline-none w-full mr-6 text-sm p-1 rounded"
                            value={node.title}
                            onChange={(e) => onUpdate(node.id, { title: e.target.value })}
                            onFocus={onInputFocus}
                            autoFocus
                        />
                    ) : (
                        <div 
                            className="font-bold text-slate-800 cursor-text truncate mr-6 text-sm"
                            onDoubleClick={() => onSetEditing(node.id)}
                        >
                            {node.title}
                        </div>
                    )}

                    <button 
                        onClick={(e) => { e.stopPropagation(); onToggleColorPicker(node.id); }}
                        className="p-1 rounded-full hover:bg-black/10 text-slate-400 hover:text-indigo-600 transition-colors absolute right-0 top-0"
                    >
                        <Palette size={14} />
                    </button>

                    {activeColorPickerId === node.id && (
                        <div className="absolute right-[-10px] top-8 bg-white rounded-lg shadow-xl border border-slate-200 p-3 z-50 w-48 animate-in fade-in zoom-in-95 duration-100 cursor-default" onMouseDown={(e) => e.stopPropagation()}>
                            <div className="grid grid-cols-4 gap-2 mb-3">
                                {savedColors.map(color => (
                                    <button 
                                        key={color}
                                        onClick={() => onUpdate(node.id, { color })}
                                        className={`w-8 h-8 rounded-full border border-slate-200 shadow-sm transition-transform hover:scale-110 ${node.color === color ? 'ring-2 ring-offset-1 ring-indigo-400' : ''}`}
                                        style={{ backgroundColor: color }}
                                    />
                                ))}
                            </div>
                            <div className="flex items-center gap-2 pt-2 border-t border-slate-100">
                                <div className="relative w-8 h-8 rounded-full overflow-hidden border border-slate-300 shadow-inner">
                                    <input 
                                    type="color" 
                                    className="absolute -top-2 -left-2 w-16 h-16 cursor-pointer"
                                    value={node.color || '#ffffff'}
                                    onChange={(e) => onUpdate(node.id, { color: e.target.value })}
                                    />
                                </div>
                                <button 
                                onClick={() => onSaveColor(node.color || '#ffffff')}
                                className="text-[10px] font-bold text-indigo-600 hover:text-indigo-800 hover:underline flex-1 text-right"
                                >
                                + SAUVER
                                </button>
                            </div>
                        </div>
                    )}
                </div>

                <div className="flex-1 overflow-y-auto custom-scrollbar relative mb-4">
                    {isEditing ? (
                        <textarea
                            className={`w-full h-full bg-white/70 resize-none outline-none text-xs leading-relaxed p-2 rounded border border-indigo-100 shadow-inner ${node.type === 'dialogue' ? 'font-mono text-slate-700' : 'text-slate-600'}`}
                            placeholder={node.type === 'dialogue' ? "Héros: Salut !\nGuide: ..." : "Résumé de l'intrigue..."}
                            value={node.description}
                            onChange={(e) => onInputCheckAutocomplete(e, node.id, 'description')}
                            onKeyDown={(e) => onKeyDownInInput(e, node.id)}
                            onFocus={onInputFocus}
                            onBlur={() => onSetEditing(null)}
                        />
                    ) : (
                        <div 
                            className={`w-full h-full text-xs text-slate-600 leading-relaxed p-1 cursor-text whitespace-pre-wrap ${node.type === 'dialogue' ? 'font-mono bg-indigo-50/30 rounded pl-2 border-l-2 border-indigo-200' : ''}`}
                            onClick={() => onSetEditing(node.id)}
                        >
                            {richDescription}
                        </div>
                    )}
                </div>

                <div className="absolute bottom-2 right-2 z-20">
                    {showTypePicker && (
                        <div className="absolute bottom-full mb-2 right-0 bg-white shadow-xl border border-slate-200 rounded-lg p-1 flex gap-1 animate-in zoom-in-95 duration-100 w-max" onMouseDown={(e) => e.stopPropagation()}>
                            <button
                                onClick={(e) => { e.stopPropagation(); onUpdate(node.id, { type: 'story' }); setShowTypePicker(false); }}
                                className={`p-1.5 rounded hover:bg-slate-100 ${node.type === 'story' ? 'bg-indigo-50 ring-1 ring-indigo-200' : ''}`}
                                title="Narration"
                            >
                                <BookOpen size={14} className="text-slate-500" />
                            </button>
                            <button
                                onClick={(e) => { e.stopPropagation(); onUpdate(node.id, { type: 'action' }); setShowTypePicker(false); }}
                                className={`p-1.5 rounded hover:bg-amber-50 ${node.type === 'action' ? 'bg-amber-50 ring-1 ring-amber-200' : ''}`}
                                title="Action"
                            >
                                <Zap size={14} className="text-amber-500" />
                            </button>
                            <button
                                onClick={(e) => { e.stopPropagation(); onUpdate(node.id, { type: 'dialogue' }); setShowTypePicker(false); }}
                                className={`p-1.5 rounded hover:bg-blue-50 ${node.type === 'dialogue' ? 'bg-blue-50 ring-1 ring-blue-200' : ''}`}
                                title="Dialogue"
                            >
                                <MessageCircle size={14} className="text-blue-500" />
                            </button>
                        </div>
                    )}

                    <button
                        className="p-1.5 rounded-full bg-white/70 hover:bg-white shadow-sm border border-slate-100 hover:border-indigo-200 transition-all opacity-80 group-hover:opacity-100"
                        onClick={(e) => { e.stopPropagation(); setShowTypePicker(!showTypePicker); }}
                    >
                        {node.type === 'story' && <BookOpen size={14} className="text-slate-500" />}
                        {node.type === 'action' && <Zap size={14} className="text-amber-500" />}
                        {node.type === 'dialogue' && <MessageCircle size={14} className="text-blue-500" />}
                    </button>
                </div>
            </div>

            <button
                className="absolute -right-3 top-1/2 -translate-y-1/2 w-6 h-6 bg-white border border-slate-300 rounded-full flex items-center justify-center text-slate-400 hover:text-indigo-600 hover:border-indigo-500 shadow-sm opacity-0 group-hover:opacity-100 transition-all z-20"
                onMouseDown={(e) => onStartConnection(e, node.id)}
            >
                <ArrowRight size={12} />
            </button>
        </div>
    );
}, (prev, next) => {
    return (
        prev.node === next.node && 
        prev.isSelected === next.isSelected &&
        prev.isEditing === next.isEditing &&
        prev.activeColorPickerId === next.activeColorPickerId &&
        prev.entities === next.entities
    );
});

interface SuggestionState {
    active: boolean;
    trigger: string;
    query: string;
    nodeId: string;
    field: 'title' | 'description';
    cursorIndex: number;
    selectedIndex: number;
    filteredEntities: Entity[];
}

const StoryWorkflow: React.FC<StoryWorkflowProps> = ({ data, onUpdate, entities, onNavigateToEntity }) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const rafRef = useRef<number | null>(null);
  
  const [internalNodes, setInternalNodes] = useState<PlotNode[]>(data.nodes);
  const internalNodesRef = useRef(internalNodes);
  useEffect(() => { internalNodesRef.current = internalNodes; }, [internalNodes]);

  useEffect(() => {
      setInternalNodes(data.nodes);
  }, [data.nodes]);

  useEffect(() => {
      return () => {
          if (rafRef.current) cancelAnimationFrame(rafRef.current);
      };
  }, []);

  const [activeSuggestion, setActiveSuggestion] = useState<SuggestionState | null>(null);
  const [selectedNodeIds, setSelectedNodeIds] = useState<Set<string>>(new Set());
  const [savedColors, setSavedColors] = useState<string[]>(INITIAL_COLORS);
  const [activeColorPickerId, setActiveColorPickerId] = useState<string | null>(null);
  const [editingNodeId, setEditingNodeId] = useState<string | null>(null);
  const [history, setHistory] = useState<WorkflowData[]>([]);
  
  const [isDragging, setIsDragging] = useState(false);
  const [dragStartPositions, setDragStartPositions] = useState<Map<string, {x: number, y: number}>>(new Map());
  const [dragStartMouse, setDragStartMouse] = useState({ x: 0, y: 0 });

  const [connectingNodeId, setConnectingNodeId] = useState<string | null>(null);
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 });

  const [isPanning, setIsPanning] = useState(false);
  const [panStart, setPanStart] = useState({ x: 0, y: 0 });
  const [scrollStart, setScrollStart] = useState({ x: 0, y: 0 });

  const pushHistory = useCallback(() => {
    setHistory(prev => {
      const newHistory = [...prev, data];
      if (newHistory.length > 20) return newHistory.slice(newHistory.length - 20);
      return newHistory;
    });
  }, [data]);

  const updateNode = useCallback((id: string, updates: Partial<PlotNode>) => {
    const currentNodes = internalNodesRef.current;
    onUpdate({
      ...data,
      nodes: currentNodes.map(n => n.id === id ? { ...n, ...updates } : n)
    });
  }, [data, onUpdate]);

  const handleInputFocus = useCallback((e: React.FocusEvent) => {
    e.stopPropagation();
  }, []);

  const handleInputWithAutocomplete = useCallback((
      e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>, 
      nodeId: string, 
      field: 'title' | 'description'
  ) => {
      const val = e.target.value;
      updateNode(nodeId, { [field]: val });
      
      const cursor = e.target.selectionStart || 0;
      const textBeforeCursor = val.slice(0, cursor);
      const match = textBeforeCursor.match(/([@#^])([^@#^\s]*)$/);

      if (match) {
          const trigger = match[1];
          const query = match[2].toLowerCase();
          const targetType = trigger === '@' ? EntityType.CHARACTER : trigger === '#' ? EntityType.LOCATION : EntityType.OBJECT;
          const filtered = entities.filter(ent => 
              ent.type === targetType && 
              ent.name.toLowerCase().includes(query)
          );

          setActiveSuggestion({
              active: true,
              trigger,
              query,
              nodeId,
              field,
              cursorIndex: cursor,
              selectedIndex: 0,
              filteredEntities: filtered
          });
      } else {
          setActiveSuggestion(null);
      }
  }, [updateNode, entities]);

  const insertEntity = (entity: Entity) => {
      if (!activeSuggestion) return;
      const { nodeId, field, trigger, query } = activeSuggestion;
      const node = internalNodesRef.current.find(n => n.id === nodeId);
      if (!node) return;
      const currentText = node[field] as string;
      const cursor = activeSuggestion.cursorIndex;
      const insertionLength = trigger.length + query.length;
      const startIdx = cursor - insertionLength;
      if (startIdx < 0) return;
      const before = currentText.slice(0, startIdx);
      const after = currentText.slice(cursor);
      const isDialogue = node.type === 'dialogue' && activeSuggestion.trigger === '@';
      const suffix = isDialogue ? ": " : " ";
      updateNode(nodeId, { [field]: before + entity.name + suffix + after });
      setActiveSuggestion(null);
  };

  const handleKeyDownInInput = useCallback((e: React.KeyboardEvent, nodeId: string) => {
      if (activeSuggestion && activeSuggestion.nodeId === nodeId) {
          if (e.key === 'ArrowDown') {
              e.preventDefault();
              setActiveSuggestion(prev => prev ? { ...prev, selectedIndex: (prev.selectedIndex + 1) % prev.filteredEntities.length } : null);
              return;
          } else if (e.key === 'ArrowUp') {
              e.preventDefault();
              setActiveSuggestion(prev => prev ? { ...prev, selectedIndex: (prev.selectedIndex - 1 + prev.filteredEntities.length) % prev.filteredEntities.length } : null);
              return;
          } else if (e.key === 'Tab' || e.key === 'Enter') {
              e.preventDefault();
              if (activeSuggestion.filteredEntities.length > 0) {
                  insertEntity(activeSuggestion.filteredEntities[activeSuggestion.selectedIndex]);
              } else {
                  setActiveSuggestion(null); 
              }
              return;
          } else if (e.key === 'Escape') {
              setActiveSuggestion(null);
              return;
          }
      }
  }, [activeSuggestion, entities, updateNode]);

  const handleNodeMouseDown = useCallback((e: React.MouseEvent, nodeId: string) => {
    e.stopPropagation(); 
    setActiveColorPickerId(null);

    setSelectedNodeIds(prevSelected => {
        const newSelection = new Set(prevSelected);
        if (e.ctrlKey) {
            if (newSelection.has(nodeId)) newSelection.delete(nodeId);
            else newSelection.add(nodeId);
        } else {
            if (!newSelection.has(nodeId)) {
                newSelection.clear();
                newSelection.add(nodeId);
            }
        }
        const finalDragIds = e.ctrlKey ? newSelection : (newSelection.has(nodeId) ? newSelection : new Set([nodeId]));
        const startPositions = new Map<string, {x: number, y: number}>();
        internalNodesRef.current.forEach(n => {
            if (finalDragIds.has(n.id)) {
                startPositions.set(n.id, { x: n.x, y: n.y });
            }
        });
        setDragStartPositions(startPositions);
        return newSelection;
    });

    setIsDragging(true);
    setDragStartMouse({ x: e.clientX, y: e.clientY });
    pushHistory();
  }, [pushHistory]);

  const startConnection = useCallback((e: React.MouseEvent, nodeId: string) => {
    e.stopPropagation();
    pushHistory();
    setConnectingNodeId(nodeId);
  }, [pushHistory]);

  const finishConnection = useCallback((e: React.MouseEvent, targetId: string) => {
    if (connectingNodeId && connectingNodeId !== targetId) {
        const exists = data.connections.some(c => c.source === connectingNodeId && c.target === targetId);
        if (!exists) {
                const newConn: PlotConnection = {
                id: `conn-${Date.now()}`,
                source: connectingNodeId,
                target: targetId
            };
            onUpdate({
                ...data,
                nodes: internalNodesRef.current,
                connections: [...data.connections, newConn]
            });
        }
    }
    setConnectingNodeId(null);
  }, [data, onUpdate, connectingNodeId]);

  const handleToggleColorPicker = useCallback((id: string) => {
      setActiveColorPickerId(prev => prev === id ? null : id);
  }, []);

  const handleSaveColor = useCallback((color: string) => {
      setSavedColors(prev => !prev.includes(color) ? [...prev, color] : prev);
  }, []);

  const handleMouseMove = (e: React.MouseEvent) => {
    const rect = containerRef.current?.getBoundingClientRect();
    if (!rect) return;
    const clientX = e.clientX;
    const clientY = e.clientY;

    if (isPanning && containerRef.current) {
        const dx = clientX - panStart.x;
        const dy = clientY - panStart.y;
        containerRef.current.scrollLeft = scrollStart.x - dx;
        containerRef.current.scrollTop = scrollStart.y - dy;
        return;
    }

    const scrollLeft = containerRef.current?.scrollLeft || 0;
    const scrollTop = containerRef.current?.scrollTop || 0;
    setMousePos({ x: clientX - rect.left + scrollLeft, y: clientY - rect.top + scrollTop });

    if (isDragging) {
      if (rafRef.current) return;
      rafRef.current = requestAnimationFrame(() => {
          const dx = clientX - dragStartMouse.x;
          const dy = clientY - dragStartMouse.y;
          setInternalNodes(prevNodes => prevNodes.map(node => {
            const startPos = dragStartPositions.get(node.id);
            if (startPos) return { ...node, x: startPos.x + dx, y: startPos.y + dy };
            return node;
          }));
          rafRef.current = null;
      });
    }
  };

  const handleMouseUp = () => {
    if (rafRef.current) {
        cancelAnimationFrame(rafRef.current);
        rafRef.current = null;
    }
    if (isDragging) onUpdate({ ...data, nodes: internalNodesRef.current });
    setIsDragging(false);
    setIsPanning(false);
    setConnectingNodeId(null);
  };

  const handleCanvasMouseDown = (e: React.MouseEvent) => {
    if (!e.ctrlKey) setSelectedNodeIds(new Set());
    setActiveSuggestion(null); 
    setActiveColorPickerId(null); 
    setEditingNodeId(null); 
    setIsPanning(true);
    setPanStart({ x: e.clientX, y: e.clientY });
    if (containerRef.current) {
        setScrollStart({ x: containerRef.current.scrollLeft, y: containerRef.current.scrollTop });
    }
  };

  const handleCanvasDoubleClick = (e: React.MouseEvent) => {
    e.preventDefault();
    const rect = containerRef.current?.getBoundingClientRect();
    if (!rect) return;
    const x = e.clientX - rect.left + (containerRef.current?.scrollLeft || 0) - CARD_WIDTH / 2;
    const y = e.clientY - rect.top + (containerRef.current?.scrollTop || 0) - CARD_HEIGHT / 2;
    pushHistory();
    const newNode: PlotNode = {
      id: `node-${Date.now()}`,
      x,
      y,
      title: 'Nouvel événement',
      description: '',
      color: INITIAL_COLORS[0],
      type: 'story'
    };
    onUpdate({ ...data, nodes: [...internalNodesRef.current, newNode] });
    setSelectedNodeIds(new Set([newNode.id]));
    setEditingNodeId(newNode.id);
  };

  const handleDeleteSelected = () => {
      if (selectedNodeIds.size === 0) return;
      pushHistory();
      const newNodes = internalNodes.filter(n => !selectedNodeIds.has(n.id));
      const newConnections = data.connections.filter(c => !selectedNodeIds.has(c.source) && !selectedNodeIds.has(c.target));
      onUpdate({ nodes: newNodes, connections: newConnections });
      setSelectedNodeIds(new Set());
  };

  const handleAddNodeCenter = () => {
      pushHistory();
      const scrollLeft = containerRef.current?.scrollLeft || 0;
      const scrollTop = containerRef.current?.scrollTop || 0;
      const clientWidth = containerRef.current?.clientWidth || 800;
      const clientHeight = containerRef.current?.clientHeight || 600;
      const newNode: PlotNode = {
        id: `node-${Date.now()}`,
        x: scrollLeft + clientWidth / 2 - CARD_WIDTH / 2,
        y: scrollTop + clientHeight / 2 - CARD_HEIGHT / 2,
        title: 'Nouveau point d\'intrigue',
        description: '',
        color: INITIAL_COLORS[0],
        type: 'story'
      };
      onUpdate({ ...data, nodes: [...internalNodesRef.current, newNode] });
      setSelectedNodeIds(new Set([newNode.id]));
      setEditingNodeId(newNode.id);
  };

  return (
    <div className="h-full flex flex-col overflow-hidden bg-[#eef2ff] relative">
      <div className="h-12 bg-white border-b border-indigo-100 flex items-center justify-between px-4 z-10 shadow-sm shrink-0">
        <div className="flex items-center gap-2">
            <button onClick={handleAddNodeCenter} className="flex items-center gap-1.5 px-3 py-1.5 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 text-xs font-bold transition-all shadow-md shadow-indigo-100">
                <Plus size={14} /> AJOUTER NŒUD
            </button>
            <div className="w-px h-6 bg-slate-100 mx-2" />
            <div className="text-[10px] uppercase font-bold text-slate-400 tracking-wider">
                {selectedNodeIds.size > 0 ? `${selectedNodeIds.size} SÉLECTIONNÉ(S)` : 'Double-cliquez sur le canvas pour créer'}
            </div>
        </div>
        <div className="flex items-center gap-2">
            <button onClick={handleDeleteSelected} disabled={selectedNodeIds.size === 0} className="p-2 text-red-500 hover:bg-red-50 rounded-lg disabled:opacity-30 transition-colors" title="Supprimer">
                <Trash2 size={16} />
            </button>
        </div>
      </div>

      <div 
        ref={containerRef}
        className="flex-1 overflow-auto relative cursor-grab active:cursor-grabbing bg-[#eef2ff]"
        onMouseDown={handleCanvasMouseDown}
        onMouseMove={handleMouseMove}
        onMouseUp={handleMouseUp}
        onDoubleClick={handleCanvasDoubleClick}
        style={{ 
            backgroundImage: 'radial-gradient(#d1d5db 1px, transparent 1px)', 
            backgroundSize: '24px 24px' 
        }}
      >
        <svg className="absolute top-0 left-0 w-[4000px] h-[4000px] pointer-events-none z-0">
            {data.connections.map(conn => {
                const source = internalNodes.find(n => n.id === conn.source); 
                const target = internalNodes.find(n => n.id === conn.target); 
                if (!source || !target) return null;
                const startX = source.x + CARD_WIDTH / 2;
                const startY = source.y + CARD_HEIGHT / 2;
                const endX = target.x + CARD_WIDTH / 2;
                const endY = target.y + CARD_HEIGHT / 2;
                return (
                    <line key={conn.id} x1={startX} y1={startY} x2={endX} y2={endY} stroke="#cbd5e1" strokeWidth="2" markerEnd="url(#arrowhead)" />
                );
            })}
            {connectingNodeId && (
                <line 
                    x1={(internalNodes.find(n => n.id === connectingNodeId)?.x || 0) + CARD_WIDTH/2}
                    y1={(internalNodes.find(n => n.id === connectingNodeId)?.y || 0) + CARD_HEIGHT/2}
                    x2={mousePos.x} y2={mousePos.y}
                    stroke="#6366f1" strokeWidth="2" strokeDasharray="5,5" markerEnd="url(#arrowhead-blue)"
                />
            )}
            <defs>
                <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="28" refY="3.5" orient="auto">
                    <path d="M0,0 L0,7 L10,3.5 Z" fill="#cbd5e1" />
                </marker>
                <marker id="arrowhead-blue" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
                    <path d="M0,0 L0,7 L10,3.5 Z" fill="#6366f1" />
                </marker>
            </defs>
        </svg>

        {internalNodes.map(node => (
            <StoryNode
                key={node.id}
                node={node}
                isSelected={selectedNodeIds.has(node.id)}
                isEditing={editingNodeId === node.id}
                activeColorPickerId={activeColorPickerId}
                entities={entities}
                savedColors={savedColors}
                onMouseDown={handleNodeMouseDown}
                onMouseUp={finishConnection}
                onStartConnection={startConnection}
                onUpdate={updateNode}
                onSetEditing={setEditingNodeId}
                onToggleColorPicker={handleToggleColorPicker}
                onSaveColor={handleSaveColor}
                onNavigateToEntity={onNavigateToEntity}
                onInputFocus={handleInputFocus}
                onInputCheckAutocomplete={handleInputWithAutocomplete}
                onKeyDownInInput={handleKeyDownInInput}
            />
        ))}
      </div>

      {activeSuggestion && (
          <div className="fixed z-50 bg-white rounded-xl shadow-2xl border border-indigo-100 w-64 max-h-48 overflow-y-auto" style={{ left: '50%', top: '50%', transform: 'translate(-50%, -50%)' }}>
              <div className="px-3 py-2 bg-indigo-600 text-white text-[10px] font-black uppercase tracking-widest">
                  Insérer {activeSuggestion.trigger === '@' ? 'Personnage' : activeSuggestion.trigger === '#' ? 'Lieu' : 'Objet'}
              </div>
              <div className="divide-y divide-slate-50">
                  {activeSuggestion.filteredEntities.length > 0 ? (
                      activeSuggestion.filteredEntities.map((ent, idx) => (
                          <button
                              key={ent.id}
                              className={`w-full text-left px-4 py-3 text-xs flex items-center gap-3 hover:bg-indigo-50 transition-colors ${idx === activeSuggestion.selectedIndex ? 'bg-indigo-50 text-indigo-700 font-bold' : 'text-slate-700'}`}
                              onClick={() => insertEntity(ent)}
                          >
                              {ent.name}
                          </button>
                      ))
                  ) : (
                      <div className="p-4 text-xs text-slate-400 italic text-center">Aucun résultat</div>
                  )}
              </div>
          </div>
      )}
    </div>
  );
};

export default StoryWorkflow;
