import React, { useCallback, useState, useRef, useEffect, useMemo } from 'react';
import ReactFlow, {
  Node,
  Edge,
  Controls,
  Background,
  useNodesState,
  useEdgesState,
  addEdge,
  Connection,
  Panel,
  NodeMouseHandler,
  ReactFlowInstance,
  MiniMap,
  NodeTypes,
} from 'reactflow';
import 'reactflow/dist/style.css';
import axios from '../utils/axios';
import SidePane from './SidePane';
import { v4 as uuidv4 } from 'uuid';
import EditableColorNode from './EditableColorNode';
import { saveMindMapToJSON, loadMindMapFromJSON, MindMapData } from '../utils/jsonUtils';
import styles from './MindMapEditor.module.css';
import * as MdIcons from 'react-icons/md';
import ChatModal from './ChatModal';

// Types
interface NodeData {
  label: string;
  bgColor: string;
  textColor: string;
  onChange: (nodeId: string, data: Partial<NodeData>) => void;
}

interface HistoryState {
  nodes: Node[];
  edges: Edge[];
  config: any;
}

// Constants
const MAX_HISTORY_SIZE = 50;
const AUTO_SAVE_DELAY = 1000;

const initialEdges: Edge[] = [];

const FALLBACK_COLOR_CONFIG = { nodeBgColor: '#ffffff', nodeTextColor: '#000000' };

const initialNodesWithHandler: Node[] = [
  {
    id: '1',
    type: 'editableColor',
    data: { 
      label: 'Main Idea', 
      bgColor: FALLBACK_COLOR_CONFIG.nodeBgColor,
      textColor: FALLBACK_COLOR_CONFIG.nodeTextColor,
      onChange: () => {} // Will be replaced with actual handler
    },
    position: { x: 250, y: 25 },
  },
];

const nodeTypes: NodeTypes = {
  editableColor: EditableColorNode,
};

// Add color presets
const COLOR_PRESETS = [
  { name: 'Default', bg: '#ffffff', text: '#000000' },
  { name: 'Dark', bg: '#2d3748', text: '#ffffff' },
  { name: 'Light Blue', bg: '#ebf8ff', text: '#2b6cb0' },
  { name: 'Warm', bg: '#fffaf0', text: '#744210' },
  { name: 'Cool', bg: '#f0fff4', text: '#22543d' },
  { name: 'Modern', bg: '#f7fafc', text: '#1a202c' },
];

const MindMapEditor = () => {
  // State declarations
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
  const [currentMindMapId, setCurrentMindMapId] = useState<string | null>(null);
  const [isSaving, setIsSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [mindMapName, setMindMapName] = useState<string>('Untitled Mind Map');
  const [isSidePaneCollapsed, setIsSidePaneCollapsed] = useState(false);
  const [isDirty, setIsDirty] = useState(false);
  const [errorStack, setErrorStack] = useState<string[]>([]);
  const [history, setHistory] = useState<HistoryState[]>([]);
  const [future, setFuture] = useState<HistoryState[]>([]);
  const [config, setConfig] = useState(() => FALLBACK_COLOR_CONFIG);
  const [saveTimeout, setSaveTimeout] = useState<NodeJS.Timeout | null>(null);
  const reactFlowWrapper = useRef<HTMLDivElement>(null);
  const [reactFlowInstance, setReactFlowInstance] = useState<ReactFlowInstance | null>(null);
  const [selectedNode, setSelectedNode] = useState<Node | null>(null);
  const [isEditing, setIsEditing] = useState(false);
  const [editValue, setEditValue] = useState('');
  const [editPosition, setEditPosition] = useState({ x: 0, y: 0 });
  const editInputRef = useRef<HTMLInputElement>(null);
  const [isHistoryEnabled, setIsHistoryEnabled] = useState(true);
  const [isLoading, setIsLoading] = useState(false);
  const [showColorPicker, setShowColorPicker] = useState(false);
  const [isChatOpen, setIsChatOpen] = useState(false);
  const [selectedNodes, setSelectedNodes] = useState<Node[]>([]);
  const [copiedNodes, setCopiedNodes] = useState<Node[] | null>(null);

  // Refs
  const errorTimeout = useRef<NodeJS.Timeout | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);

  // Node data change handler - Moved before initialNodes
  const handleNodeDataChange = useCallback((nodeId: string, newData: Partial<NodeData>) => {
    setNodes((nds) =>
      nds.map((node) => {
        if (node.id === nodeId) {
          return {
            ...node,
            data: {
              ...node.data,
              ...newData,
              bgColor: newData.bgColor || config.nodeBgColor,
              textColor: newData.textColor || config.nodeTextColor,
            },
          };
        }
        return node;
      })
    );
    setIsDirty(true);
  }, [setNodes, config]);

  // Memoized initial nodes
  const initialNodes = useMemo(() => [{
    id: '1',
    type: 'editableColor',
    data: { 
      label: 'Main Idea', 
      bgColor: config.nodeBgColor,
      textColor: config.nodeTextColor,
      onChange: handleNodeDataChange
    },
    position: { x: 250, y: 25 },
  }], [handleNodeDataChange]);

  // Initialize nodes with handler
  useEffect(() => {
    setNodes(initialNodes);
  }, [initialNodes, setNodes]);

  // History management
  const pushHistory = useCallback((state: HistoryState) => {
    setHistory((h) => {
      const newHistory = [...h, state];
      if (newHistory.length > MAX_HISTORY_SIZE) {
        return newHistory.slice(-MAX_HISTORY_SIZE);
      }
      return newHistory;
    });
    setFuture([]);
  }, []);

  // Error handling
  const addError = useCallback((msg: string, details?: string) => {
    const errorMessage = details ? `${msg}: ${details}` : msg;
    setErrorStack((stack) => [...stack, errorMessage]);
  }, []);

  const removeError = useCallback((idx: number) => {
    setErrorStack((stack) => stack.filter((_, i) => i !== idx));
  }, []);

  // Auto-save functionality
  const debouncedAutoSave = useCallback(async () => {
    if (saveTimeout) {
      clearTimeout(saveTimeout);
    }

    const timeout = setTimeout(async () => {
      if (!isDirty) return;

      setIsSaving(true);
      try {
        const data = {
          nodes,
          edges,
          name: mindMapName,
          config,
        };

        const apiBase = process.env.REACT_APP_API_URL || '';
        if (currentMindMapId) {
          await axios.put(`${apiBase}/api/workflows/${currentMindMapId}`, data);
        } else {
          const response = await axios.post(`${apiBase}/api/workflows`, data);
          setCurrentMindMapId(response.data.id);
        }
        setIsDirty(false);
      } catch (error) {
        addError('Failed to save mind map', error instanceof Error ? error.message : 'Unknown error');
      } finally {
        setIsSaving(false);
      }
    }, 1000);

    setSaveTimeout(timeout);
  }, [isDirty, nodes, edges, mindMapName, config, currentMindMapId, addError]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (saveTimeout) {
        clearTimeout(saveTimeout);
      }
      if (errorTimeout.current) {
        clearTimeout(errorTimeout.current);
      }
    };
  }, [saveTimeout]);

  // Event handlers
  const handleConfigChange = useCallback((newConfig: any) => {
    const currentState = { nodes, edges, config };
    pushHistory(currentState);
    setConfig(newConfig);
    setNodes((nds) => nds.map((node: any) => ({
      ...node,
      data: {
        ...node.data,
        bgColor: newConfig.nodeBgColor,
        textColor: newConfig.nodeTextColor,
      },
    })));
    setIsDirty(true);
  }, [nodes, edges, config, pushHistory, setNodes]);

  const handleNameChange = useCallback(async (newName: string) => {
    if (newName.trim().length === 0) {
      addError('Mind map name cannot be empty');
      return;
    }
    setMindMapName(newName);
    setIsDirty(true);
  }, [addError]);

  const handleSelectMindMap = useCallback(async (mindMap: any) => {
    setIsLoading(true);
    try {
      setError(null);
      const loadedConfig = mindMap.config || FALLBACK_COLOR_CONFIG;
      React.startTransition(() => {
        setConfig(loadedConfig);
        setNodes(
          mindMap.nodes.map((node: any) => ({
              ...node,
              type: 'editableColor',
              data: {
                ...node.data,
                onChange: handleNodeDataChange,
              },
          }))
        );
        setEdges(mindMap.edges);
        setCurrentMindMapId(mindMap.id);
        setMindMapName(mindMap.name || 'Untitled Mind Map');
      });
    } catch (error) {
      console.error('Error loading mind map:', error);
      addError('Failed to load mind map', error instanceof Error ? error.message : 'Unknown error');
    } finally {
      setIsLoading(false);
    }
  }, [setNodes, setEdges, handleNodeDataChange, addError]);

  const handleNewMindMap = useCallback(() => {
    setConfig(config || FALLBACK_COLOR_CONFIG);
    setNodes([
      {
        id: '1',
        type: 'editableColor',
        data: {
          label: 'Main Idea',
          bgColor: (config && config.nodeBgColor) || FALLBACK_COLOR_CONFIG.nodeBgColor,
          textColor: (config && config.nodeTextColor) || FALLBACK_COLOR_CONFIG.nodeTextColor,
          onChange: handleNodeDataChange,
        },
        position: { x: 250, y: 25 },
      },
    ]);
    setEdges([]);
    setCurrentMindMapId(null);
    setMindMapName('Untitled Mind Map');
    setError(null);
  }, [setNodes, setEdges, handleNodeDataChange, config]);

  const handleExportJSON = useCallback(() => {
    try {
      const mindMapData: MindMapData = {
        id: currentMindMapId || uuidv4(),
        name: mindMapName,
        updatedAt: new Date().toISOString(),
        nodes,
        edges,
        config,
      };
      saveMindMapToJSON(mindMapData);
    } catch (error) {
      addError('Failed to export mind map', error instanceof Error ? error.message : 'Unknown error');
    }
  }, [currentMindMapId, mindMapName, nodes, edges, config, addError]);

  const handleImportJSON = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target?.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const json = JSON.parse(e.target?.result as string);
        if (json.nodes && json.edges) {
          const importedNodes = json.nodes.map((node: any) => ({
            ...node,
            type: 'editableColor',
            data: {
              ...node.data,
              onChange: handleNodeDataChange,
              bgColor: node.data?.bgColor || (json.config?.nodeBgColor || FALLBACK_COLOR_CONFIG.nodeBgColor),
              textColor: node.data?.textColor || (json.config?.nodeTextColor || FALLBACK_COLOR_CONFIG.nodeTextColor),
            },
          }));
          setNodes(importedNodes);
          setEdges(json.edges);
        } else {
          addError('Invalid mind map JSON: Missing nodes or edges');
        }
        if (json.config) setConfig(json.config);
        if (json.name) setMindMapName(json.name);
      } catch (err) {
        addError('Invalid mind map JSON', err instanceof Error ? err.message : 'Unknown error');
      }
    };
    reader.readAsText(file);
  };

  const handleDeleteMindMap = useCallback(async (id: string) => {
    if (currentMindMapId === id) {
      // Create a new mind map before deleting the current one
      const data = {
        nodes,
        edges,
        name: mindMapName,
        config,
      };
      try {
        const response = await axios.post('/api/workflows', data);
        setCurrentMindMapId(response.data.id);
        setIsDirty(false);
      } catch (error) {
        console.error('Error creating new mind map:', error);
        addError('Failed to create new mind map');
      }
    }
  }, [currentMindMapId, nodes, edges, mindMapName, config, addError]);

  // Effects
  useEffect(() => {
    if (isDirty) {
      debouncedAutoSave();
    }
  }, [isDirty, debouncedAutoSave]);

  useEffect(() => {
    if (errorStack.length > 0) {
      const timeout = setTimeout(() => removeError(0), 3000);
      return () => clearTimeout(timeout);
    }
  }, [errorStack, removeError]);

  useEffect(() => {
    return () => {
      if (saveTimeout) {
        clearTimeout(saveTimeout);
      }
    };
  }, [saveTimeout]);

  const onConnect = useCallback(
    (params: Connection) => {
      setEdges((eds) => addEdge(params, eds));
    },
    [setEdges]
  );

  const onAddNode = useCallback(() => {
    pushHistory({ nodes, edges, config });
    const newNode: Node = {
      id: uuidv4(),
      type: 'editableColor',
      data: { label: `Node ${nodes.length + 1}`, bgColor: config.nodeBgColor, textColor: config.nodeTextColor, onChange: handleNodeDataChange },
      position: {
        x: Math.random() * 500,
        y: Math.random() * 500,
      },
    };
    setNodes((nds) => [...nds, newNode]);
    setIsDirty(true);
  }, [nodes.length, setNodes, handleNodeDataChange, config, pushHistory]);

  const undo = useCallback(() => {
    if (history.length > 0) {
      setFuture((f) => [ { nodes, edges, config }, ...f ]);
      const prev = history[history.length - 1];
      setNodes(prev.nodes);
      setEdges(prev.edges);
      setConfig(prev.config);
      setHistory((h) => h.slice(0, -1));
    }
  }, [history, nodes, edges, config, setNodes, setEdges, setConfig]);

  const redo = useCallback(() => {
    if (future.length > 0) {
      setHistory((h) => [...h, { nodes, edges, config }]);
      const next = future[0];
      setNodes(next.nodes);
      setEdges(next.edges);
      setConfig(next.config);
      setFuture((f) => f.slice(1));
    }
  }, [future, nodes, edges, config, setNodes, setEdges, setConfig]);

  const zoomIn = useCallback(() => {
    reactFlowInstance?.zoomIn();
  }, [reactFlowInstance]);

  const zoomOut = useCallback(() => {
    reactFlowInstance?.zoomOut();
  }, [reactFlowInstance]);

  const centerView = useCallback(() => {
    reactFlowInstance?.fitView();
  }, [reactFlowInstance]);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
        e.preventDefault();
        undo();
      } else if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.shiftKey && e.key === 'z'))) {
        e.preventDefault();
        redo();
      } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'c') {
        // Copy selected nodes and their connecting edges
        if (selectedNodes.length > 0) {
          const selectedIds = selectedNodes.map(n => n.id);
          const copied = selectedNodes.map(n => ({ ...n, position: { ...n.position } }));
          const copiedEdges = edges.filter(e => selectedIds.includes(e.source) && selectedIds.includes(e.target));
          setCopiedNodes(copied);
          // Store copied edges in a ref for pasting
          (window as any)._copiedEdges = copiedEdges;
        }
        e.preventDefault();
      } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'v') {
        // Paste nodes and edges
        if (copiedNodes && copiedNodes.length > 0) {
          const idMap: Record<string, string> = {};
          const newNodes = copiedNodes.map(n => {
            const newId = uuidv4();
            idMap[n.id] = newId;
            return {
              ...n,
              id: newId,
              position: { x: n.position.x + 40, y: n.position.y + 40 },
              data: { ...n.data, label: n.data.label + ' (copy)', onChange: handleNodeDataChange },
            };
          });
          // Paste only edges between copied nodes
          const copiedEdges = (window as any)._copiedEdges || [];
          const newEdges = copiedEdges.map((e: Edge) => ({
            ...e,
            id: uuidv4(),
            source: idMap[e.source],
            target: idMap[e.target],
          }));
          setNodes(nds => [...nds, ...newNodes]);
          setEdges(eds => [...eds, ...newEdges]);
          setSelectedNodes(newNodes);
          setIsDirty(true);
        }
        e.preventDefault();
      } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'a') {
        setSelectedNodes(nodes);
        e.preventDefault();
      } else if ((e.key === 'Delete' || e.key === 'Backspace') && selectedNodes.length > 0) {
        const idsToDelete = selectedNodes.map(n => n.id);
        setNodes(nds => nds.filter(n => !idsToDelete.includes(n.id)));
        setEdges(eds => eds.filter(e => !idsToDelete.includes(e.source) && !idsToDelete.includes(e.target)));
        setSelectedNodes([]);
        setSelectedNode(null);
        setIsDirty(true);
        e.preventDefault();
      }
    };
    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [undo, redo, selectedNodes, copiedNodes, nodes, edges, setNodes, setEdges, handleNodeDataChange]);

  // Warn before leaving with unsaved changes
  useEffect(() => {
    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
      if (isDirty) {
        e.preventDefault();
        e.returnValue = '';
      }
    };
    window.addEventListener('beforeunload', handleBeforeUnload);
    return () => window.removeEventListener('beforeunload', handleBeforeUnload);
  }, [isDirty]);

  // Auto-dismiss error
  useEffect(() => {
    if (error) {
      if (errorTimeout.current) clearTimeout(errorTimeout.current);
      errorTimeout.current = setTimeout(() => setError(null), 3000);
    }
    return () => {
      if (errorTimeout.current) clearTimeout(errorTimeout.current);
    };
  }, [error]);

  // Add missing handlers
  const handleNodeClick: NodeMouseHandler = useCallback((event, node) => {
    if (event.ctrlKey || event.metaKey) {
      setSelectedNodes((prev) => {
        if (prev.find((n) => n.id === node.id)) {
          return prev.filter((n) => n.id !== node.id);
        } else {
          return [...prev, node];
        }
      });
    } else {
      setSelectedNodes([node]);
    }
    setSelectedNode(node);
  }, []);

  const handlePaneClick = useCallback(() => {
    setSelectedNode(null);
    setSelectedNodes([]);
  }, []);

  const handleNodeDragStop: NodeMouseHandler = useCallback((_, node) => {
    setNodes((nds) =>
      nds.map((n) => {
        if (n.id === node.id) {
          return {
            ...n,
            position: node.position,
          };
        }
        return n;
      })
    );
    setIsDirty(true);
  }, [setNodes]);

  const handleNodeDoubleClick: NodeMouseHandler = useCallback((_, node) => {
    setSelectedNode(node);
  }, []);

  // Add handlePresetSelect
  const handlePresetSelect = (preset: { name: string; bg: string; text: string }) => {
    handleConfigChange({ ...config, nodeBgColor: preset.bg, nodeTextColor: preset.text });
    setShowColorPicker(false);
  };

  return (
    <div style={{ width: '100vw', height: '100vh', display: 'flex', flexDirection: 'row' }}>
      <div style={{
        width: isSidePaneCollapsed ? '64px' : '260px',
        minWidth: isSidePaneCollapsed ? '64px' : '260px',
        maxWidth: isSidePaneCollapsed ? '64px' : '260px',
        transition: 'width 0.3s cubic-bezier(0.4,0,0.2,1)',
        height: '100%',
        zIndex: 1000,
      }}>
        <SidePane
          onSelectMindMap={handleSelectMindMap}
          onNewMindMap={handleNewMindMap}
          isCollapsed={isSidePaneCollapsed}
          setIsCollapsed={setIsSidePaneCollapsed}
          onDeleteMindMap={handleDeleteMindMap}
        />
      </div>
      <div
        style={{
          flexGrow: 1,
          minWidth: 0,
          height: '100%',
          overflow: 'auto',
          display: 'flex',
          flexDirection: 'column',
          position: 'relative',
        }}
      >
        <div className={styles.mindMapHeader}>
          <input
            type="text"
            value={mindMapName}
            onChange={e => handleNameChange(e.target.value)}
            className={styles.titleInput}
            placeholder="Untitled Mind Map"
            aria-label="Mind Map Title"
          />
        </div>
        <div className={styles.verticalToolbar}>
          {selectedNode && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginBottom: 8 }}>
              <label style={{ fontSize: 12 }}>Node Background:</label>
              <input
                type="color"
                value={selectedNode.data.bgColor}
                onChange={e => handleNodeDataChange(selectedNode.id, { bgColor: e.target.value })}
                aria-label="Node background color"
                title="Node background color"
                style={{ width: 32, height: 24, border: 'none', background: 'none' }}
              />
              <label style={{ fontSize: 12 }}>Node Text:</label>
              <input
                type="color"
                value={selectedNode.data.textColor}
                onChange={e => handleNodeDataChange(selectedNode.id, { textColor: e.target.value })}
                aria-label="Node text color"
                title="Node text color"
                style={{ width: 32, height: 24, border: 'none', background: 'none' }}
              />
            </div>
          )}
          <button
            onClick={onAddNode}
            className={styles.toolButton}
            title="Add Node"
            aria-label="Add Node"
          >
            <MdIcons.MdAdd />
          </button>
          <button
            onClick={debouncedAutoSave}
            disabled={isSaving || !isDirty}
            className={`${styles.toolButton} ${isSaving ? styles.saving : isDirty ? styles.dirty : ''}`}
            title={isSaving ? 'Saving changes' : isDirty ? 'Save changes' : 'No changes to save'}
            aria-label={isSaving ? 'Saving changes' : isDirty ? 'Save changes' : 'No changes to save'}
          >
            {isSaving ? <MdIcons.MdOutlineSaveAlt /> : isDirty ? <MdIcons.MdOutlineSaveAlt /> : <MdIcons.MdOutlineSave />}
          </button>
          <hr className={styles.toolbarDivider} />
          <button
            className={styles.toolButton}
            onClick={() => setShowColorPicker(!showColorPicker)}
            aria-label="Toggle color picker"
            aria-expanded={showColorPicker}
            aria-controls="color-picker"
            title="Change mind map colors"
          >
            <MdIcons.MdColorLens />
          </button>
          {showColorPicker && (
            <div
              id="color-picker"
              className={styles.colorPickerToolbar}
              role="dialog"
              aria-label="Color picker"
            >
              <div className={styles.colorPresets}>
                {COLOR_PRESETS.map((preset) => (
                  <button
                    key={preset.name}
                    className={styles.colorPreset}
                    onClick={() => handlePresetSelect(preset)}
                    style={{
                      background: `linear-gradient(45deg, ${preset.bg} 50%, ${preset.text} 50%)`,
                    }}
                    title={preset.name}
                    aria-label={`Select ${preset.name} color preset`}
                  />
                ))}
              </div>
              <div className={styles.customColors}>
                <div className={styles.colorControl}>
                  <label htmlFor="bgColor">Background:</label>
                  <div className={styles.colorInputWrapper}>
                    <input
                      type="color"
                      id="bgColor"
                      value={config.nodeBgColor}
                      onChange={e => handleConfigChange({ ...config, nodeBgColor: e.target.value })}
                      className={styles.colorInput}
                      title="Choose background color"
                      aria-label="Background Color"
                    />
                    <span className={styles.colorValue}>{config.nodeBgColor}</span>
                  </div>
                </div>
                <div className={styles.colorControl}>
                  <label htmlFor="textColor">Text:</label>
                  <div className={styles.colorInputWrapper}>
                    <input
                      type="color"
                      id="textColor"
                      value={config.nodeTextColor}
                      onChange={e => handleConfigChange({ ...config, nodeTextColor: e.target.value })}
                      className={styles.colorInput}
                      title="Choose text color"
                      aria-label="Text Color"
                    />
                    <span className={styles.colorValue}>{config.nodeTextColor}</span>
                  </div>
                </div>
              </div>
            </div>
          )}
          <button
            className={styles.toolButton}
            onClick={() => setIsChatOpen(true)}
            title="Open Mind Map Chat Assistant"
            aria-label="Open Mind Map Chat Assistant"
          >
            <MdIcons.MdChatBubbleOutline />
          </button>
          <button onClick={undo} className={styles.toolButton} title="Undo" aria-label="Undo"><MdIcons.MdUndo /></button>
          <button onClick={redo} className={styles.toolButton} title="Redo" aria-label="Redo"><MdIcons.MdRedo /></button>
          <button onClick={zoomIn} className={styles.toolButton} title="Zoom In" aria-label="Zoom In"><MdIcons.MdZoomIn /></button>
          <button onClick={zoomOut} className={styles.toolButton} title="Zoom Out" aria-label="Zoom Out"><MdIcons.MdZoomOut /></button>
          <button onClick={centerView} className={styles.toolButton} title="Center View" aria-label="Center View"><MdIcons.MdCenterFocusStrong /></button>
          <button
            onClick={handleExportJSON}
            className={`${styles.toolButton} ${isDirty ? styles.dirty : ''}`}
            title={isDirty ? "Save Changes" : "All Changes Saved"}
            aria-label={isDirty ? "Save Changes" : "All Changes Saved"}
          >
            <MdIcons.MdOutlineSaveAlt />
          </button>
          <button onClick={() => fileInputRef.current?.click()} className={styles.toolButton} title="Import from JSON" aria-label="Import from JSON"><MdIcons.MdFileUpload /></button>
        </div>
        <input
          type="file"
          ref={fileInputRef}
          onChange={handleImportJSON}
          accept=".json"
          style={{ display: 'none' }}
          aria-label="Import mind map"
        />
        {errorStack.map((err, idx) => (
          <div key={idx} className={styles.errorToast} role="alert">
            {err}
            <button 
              aria-label="Close error" 
              onClick={() => removeError(idx)} 
              tabIndex={0}
              className={styles.errorCloseButton}
            >
              &times;
            </button>
          </div>
        ))}
        <div style={{ flexGrow: 1, minHeight: 0 }}>
          <div className={styles.flowContainer} ref={reactFlowWrapper}>
            <ReactFlow
              nodes={nodes}
              edges={edges}
              onNodesChange={onNodesChange}
              onEdgesChange={onEdgesChange}
              onConnect={onConnect}
              onInit={setReactFlowInstance}
              onNodeClick={handleNodeClick}
              onPaneClick={handlePaneClick}
              onNodeDragStop={handleNodeDragStop}
              onNodeDoubleClick={handleNodeDoubleClick}
              nodeTypes={nodeTypes}
              fitView
              attributionPosition="bottom-right"
            >
              <Background />
              <Controls />
              <MiniMap />
            </ReactFlow>
          </div>
        </div>
      </div>
      <ChatModal 
        open={isChatOpen} 
        onClose={() => setIsChatOpen(false)} 
        onImportJSON={handleImportJSON}
        currentMindMapData={{
          nodes,
          edges,
          config,
          mindMapName
        }}
      />
    </div>
  );
};

export default MindMapEditor; 