import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
import ReactFlow, {
  MiniMap,
  Controls,
  Background,
  useNodesState,
  useEdgesState,
  addEdge,
  Panel,
  useReactFlow,
  ReactFlowProvider,
  Handle,
  Position,
  BaseEdge,
  EdgeLabelRenderer,
  getBezierPath
} from 'reactflow';
import 'reactflow/dist/style.css';

// Add custom CSS for React Flow
const generateCustomStyles = () => {
  return `
    .react-flow__node {
      z-index: 1;
    }
    
    .schema-background-node {
      z-index: -1;
      pointer-events: all;
      border-radius: 25px;
      cursor: grab;
      user-select: none;
      transition: all 0.2s ease;
    }
    
    .schema-background-node:hover {
      box-shadow: 0 0 30px rgba(0, 0, 0, 0.08);
    }
    
    .schema-background-node:active {
      cursor: grabbing;
    }
    
    .schema-background-node.dragging {
      opacity: 0.8;
    }
    
    .schema-label {
      position: absolute;
      top: 15px;
      left: 15px;
      background: rgba(255, 255, 255, 0.9);
      padding: 8px 12px;
      border-radius: 6px;
      font-size: 14px;
      font-weight: bold;
      box-shadow: 0 3px 6px rgba(0,0,0,0.1);
      display: flex;
      align-items: center;
      gap: 8px;
      z-index: 10;
    }
  `;
};

// Import icons from react-icons
import { FaDatabase, FaTable, FaArrowRight, FaExchangeAlt, FaLayerGroup } from 'react-icons/fa';
import { BiSolidData } from 'react-icons/bi';
import { AiFillFolder } from 'react-icons/ai';
import { BsFileEarmarkSpreadsheet } from 'react-icons/bs';
import { CustomProcessIcon } from './CustomIcons';

// Import mock data
import mockApiData from './mockData';

// Import ProcessForm component
import ProcessForm from './ProcessForm';

// Schema Background Node
const SchemaBackgroundNode = ({ data }) => {
  // Different background colors for each schema
  const bgColor = data.slug === 'edw_schema' 
    ? 'rgba(24, 144, 255, 0.1)' // Light blue for Schema_1
    : 'rgba(82, 196, 26, 0.1)';  // Light green for Schema_2
  
  // Border color based on schema
  const borderColor = data.slug === 'edw_schema' 
    ? '#1890ff' // Blue for Schema_1
    : '#52c41a';  // Green for Schema_2
  
  return (
    <div
      className="schema-background-node"
      style={{
        width: '100%',
        height: '100%',
        background: bgColor,
        border: `3px dashed ${data.color || borderColor}`,
        boxShadow: `0 0 20px rgba(0, 0, 0, 0.05)`,
        borderRadius: '25px', // Slightly larger border radius
      }}
    >
      <div className="schema-label" style={{ 
        color: data.color || borderColor,
        fontSize: '14px', // Slightly larger font
        padding: '8px 12px', // More padding
        background: 'rgba(255, 255, 255, 0.9)', // More opaque background
        boxShadow: '0 3px 6px rgba(0, 0, 0, 0.1)', // Stronger shadow
      }}>
        <FaLayerGroup size={14} />
        {data.name}
      </div>
    </div>
  );
};

// Custom edge with animated arrow
const CustomEdge = ({ id, source, target, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, style = {}, data, markerEnd }) => {
  const [edgePath, labelX, labelY] = getBezierPath({
    sourceX,
    sourceY,
    sourcePosition,
    targetX,
    targetY,
    targetPosition,
  });

  // Check if the process is active
  const isActive = data?.isActive !== false; // Default to active if not specified
  
  // Determine if this is a source or target edge
  const isSourceEdge = data?.edgeType === 'source';
  
  // Get the process name and mappings
  const processName = data?.processName || 'Process';
  const mappings = data?.mappings || [];
  
  // Adjust label style based on active status
  const labelBgColor = isActive ? 'white' : '#f5f5f5';
  const labelOpacity = isActive ? 1 : 0.8;
  
  return (
    <>
      <BaseEdge path={edgePath} markerEnd={markerEnd} style={style} />
      <EdgeLabelRenderer>
        <div
          style={{
            position: 'absolute',
            transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
            fontSize: 11,
            pointerEvents: 'all',
            backgroundColor: labelBgColor,
            padding: '4px 8px',
            borderRadius: '4px',
            color: isActive ? '#333' : '#666',
            fontWeight: isActive ? 500 : 400,
            boxShadow: isActive ? '0 0 5px rgba(0,0,0,0.2)' : 'none',
            border: `1px solid ${style.stroke || '#ccc'}`,
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            gap: '2px',
            maxWidth: '180px',
            opacity: labelOpacity
          }}
          className="nodrag nopan"
        >
          {/* Process name in small text */}
          <div style={{ 
            fontSize: '9px', 
            color: '#666', 
            fontStyle: 'italic',
            marginBottom: '2px',
            textAlign: 'center'
          }}>
            {processName}
            {!isActive && (
              <span style={{ 
                fontSize: '8px', 
                background: '#f0f0f0', 
                padding: '0px 3px', 
                borderRadius: '3px',
                color: '#999',
                marginLeft: '2px'
              }}>
                inactive
              </span>
            )}
          </div>
          
          {/* Column mappings */}
          <div style={{ 
            display: 'flex', 
            flexWrap: 'wrap', 
            justifyContent: 'center',
            gap: '3px'
          }}>
            {mappings.length > 0 ? (
              mappings.map((column, index) => (
                <span 
                  key={index} 
                  style={{ 
                    background: isSourceEdge 
                      ? 'rgba(82, 196, 26, 0.1)' 
                      : 'rgba(24, 144, 255, 0.1)', 
                    color: isActive 
                      ? (isSourceEdge ? '#52c41a' : '#1890ff') 
                      : '#aaaaaa',
                    padding: '1px 4px', 
                    borderRadius: '3px',
                    fontSize: '10px',
                    border: `1px solid ${isActive 
                      ? (isSourceEdge ? 'rgba(82, 196, 26, 0.3)' : 'rgba(24, 144, 255, 0.3)') 
                      : 'rgba(170, 170, 170, 0.3)'}`
                  }}
                >
                  {column}
                </span>
              ))
            ) : (
              <span style={{ color: '#999', fontSize: '10px' }}>No mappings</span>
            )}
          </div>
        </div>
      </EdgeLabelRenderer>
    </>
  );
};

// Process Node (represents ETL or data transformation)
const ProcessNode = ({ data, id }) => {
  // Function to handle process edit
  const handleProcessEdit = () => {
    // Find the process in the mock data
    const process = mockApiData.processes.find(p => p.slug === id);
    if (process) {
      // We'll use window.processEditCallback which will be set in the main component
      if (window.processEditCallback) {
        window.processEditCallback(process);
      }
    }
  };
  // Determine status color
  const statusColor = data.status === 'active' ? '#52c41a' : '#ff4d4f';
  const isActive = data.status === 'active';
  
  // Define dimensions for the SVG
  const width = 240;
  const height = 140;
  const borderWidth = 2;
  
  // Define colors
  const primaryColor = isActive ? '#fa8c16' : '#aaaaaa';
  const secondaryColor = isActive ? '#ff4d4f' : '#cccccc';
  const bgColor = '#ffffff';
  const descBgColor = isActive ? 'rgba(250, 140, 22, 0.08)' : 'rgba(170, 170, 170, 0.08)';
  
  // Create gradient ID
  const gradientId = `process-gradient-${data.label.replace(/\s+/g, '-').toLowerCase()}`;
  
  return (
    <div className="process-node" style={{ 
      position: 'relative',
      width: `${width}px`,
      height: `${height}px`,
    }}>
      {/* SVG Container */}
      <svg 
        width={width} 
        height={height} 
        viewBox={`0 0 ${width} ${height}`}
        style={{ 
          filter: isActive 
            ? 'drop-shadow(0 0 8px rgba(250, 140, 22, 0.3))' 
            : 'drop-shadow(0 0 5px rgba(170, 170, 170, 0.2))'
        }}
      >
        {/* Define gradient */}
        <defs>
          <linearGradient id={gradientId} x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%" stopColor={primaryColor} />
            <stop offset="100%" stopColor={secondaryColor} />
          </linearGradient>
        </defs>
        
        {/* Main ellipse shape */}
        <ellipse 
          cx={width / 2} 
          cy={height / 2} 
          rx={(width / 2) - borderWidth} 
          ry={(height / 2) - borderWidth}
          fill={bgColor}
          stroke={`url(#${gradientId})`}
          strokeWidth={borderWidth}
        />
        
        {/* Description background */}
        <ellipse 
          cx={width / 2} 
          cy={height - 35} 
          rx={width / 2 - 20} 
          ry={20}
          fill={descBgColor}
          stroke="none"
        />
      </svg>
      
      {/* Connection handles */}
      <Handle 
        type="target" 
        position={Position.Left} 
        id="left" 
        style={{ 
          background: primaryColor, 
          width: '10px', 
          height: '10px',
          left: '-5px',
          zIndex: 10
        }} 
        isConnectable={true}
      />
      <Handle 
        type="source" 
        position={Position.Right} 
        id="right" 
        style={{ 
          background: primaryColor, 
          width: '10px', 
          height: '10px',
          right: '-5px',
          zIndex: 10
        }} 
        isConnectable={true}
      />
      
      {/* Content overlay */}
      <div style={{
        position: 'absolute',
        top: 0,
        left: 0,
        width: '100%',
        height: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        padding: '15px',
        pointerEvents: 'auto' // Enable clicks on the content
      }}>
        {/* Icon and title */}
        <div style={{ 
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          marginBottom: '5px'
        }}>
          <div 
            onClick={handleProcessEdit}
            style={{ 
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              width: '40px',
              height: '40px',
              borderRadius: '50%',
              background: `url(#${gradientId})`,
              marginBottom: '8px',
              boxShadow: '0 3px 6px rgba(0,0,0,0.15)',
              cursor: 'pointer',
              pointerEvents: 'auto',
              zIndex: 100
            }}
          >
            <CustomProcessIcon width="24" height="24" />
          </div>
          
          <div style={{ 
            fontWeight: 'bold',
            fontSize: '14px',
            color: '#333',
            textAlign: 'center',
            maxWidth: '90%',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap'
          }}>
            {data.label}
          </div>
          
          {/* Status indicator */}
          <div style={{ 
            fontSize: '10px',
            background: statusColor,
            color: 'white',
            padding: '2px 8px',
            borderRadius: '10px',
            marginTop: '4px'
          }}>
            {data.status}
          </div>
        </div>
        
        {/* Description */}
        <div style={{ 
          fontSize: '11px',
          color: '#555',
          textAlign: 'center',
          maxWidth: '90%',
          overflow: 'hidden',
          display: '-webkit-box',
          WebkitLineClamp: 2,
          WebkitBoxOrient: 'vertical',
          marginTop: 'auto',
          paddingTop: '5px'
        }}>
          {data.description || 'Data transformation process'}
        </div>
      </div>
    </div>
  );
};

// Table Node
const TableNode = ({ data }) => {
  const isFact = data.type === 'fact';
  
  // Use a consistent color theme for all tables
  const tableColor = '#1890ff'; // Blue for all tables
  const background = '#ffffff';
  
  return (
    <div className="table-node" style={{ 
      padding: '10px', 
      borderRadius: '5px', 
      background: background, 
      border: `2px solid ${tableColor}`,
      width: '180px',
      position: 'relative',
      boxShadow: '0 0 10px rgba(24, 144, 255, 0.3)',
      color: '#333333'
    }}>
      {/* Connection handles */}
      <Handle 
        type="source" 
        position={Position.Right} 
        id="right" 
        style={{ background: tableColor, width: '10px', height: '10px' }} 
        isConnectable={true}
      />
      <Handle 
        type="target" 
        position={Position.Left} 
        id="left" 
        style={{ background: tableColor, width: '10px', height: '10px' }} 
        isConnectable={true}
      />
      
      <div style={{ fontWeight: 'bold', marginBottom: '5px', display: 'flex', alignItems: 'center' }}>
        <div style={{ display: 'flex', alignItems: 'center', flex: 1 }}>
          {/* Different icons for fact and dimension tables */}
          {isFact ? (
            <FaTable style={{ marginRight: '8px', color: tableColor, fontSize: '16px' }} />
          ) : (
            <BsFileEarmarkSpreadsheet style={{ marginRight: '8px', color: tableColor, fontSize: '16px' }} />
          )} 
          <span style={{ maxWidth: '120px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {data.label}
          </span>
          <span style={{ 
            fontSize: '10px', 
            background: tableColor, 
            color: 'white', 
            padding: '1px 4px', 
            borderRadius: '3px', 
            marginLeft: '5px',
            verticalAlign: 'middle'
          }}>
            {isFact ? 'FACT' : 'DIM'}
          </span>
        </div>
      </div>
      
      {data.columns && (
        <div style={{ 
          fontSize: '0.75em', 
          color: '#555',
          background: 'rgba(24, 144, 255, 0.1)', // Same light blue background for all tables
          padding: '5px',
          borderRadius: '3px',
          marginTop: '5px'
        }}>
          <div style={{ marginBottom: '3px', color: '#333', fontWeight: 'bold' }}>Columns:</div>
          <div style={{ 
            display: 'flex', 
            flexWrap: 'wrap', 
            gap: '3px' 
          }}>
            {data.columns.slice(0, 5).map((col, index) => (
              <span key={index} style={{ 
                background: 'rgba(0, 0, 0, 0.05)', 
                padding: '1px 4px', 
                borderRadius: '2px',
                fontSize: '10px',
                color: '#333'
              }}>
                {col}
              </span>
            ))}
            {data.columns.length > 5 && (
              <span style={{ 
                padding: '1px 4px', 
                fontSize: '10px',
                color: '#666'
              }}>
                +{data.columns.length - 5} more
              </span>
            )}
          </div>
        </div>
      )}
    </div>
  );
};

// Main DataflowCanvas component
const DataflowCanvas = () => {
  // State for nodes and edges
  const [showProcessForm, setShowProcessForm] = useState(false);
  const [selectedProcessForEdit, setSelectedProcessForEdit] = useState(null);
  const [showTableForm, setShowTableForm] = useState(false);
  const [selectedTableForEdit, setSelectedTableForEdit] = useState(null);
  
  // Get the React Flow instance
  const reactFlowInstance = useReactFlow();
  
  // Define node types
  const nodeTypes = useMemo(() => ({
    schemaBackground: SchemaBackgroundNode,
    process: ProcessNode,
    table: TableNode
  }), []);
  
  // Define edge types
  const edgeTypes = useMemo(() => ({
    custom: CustomEdge
  }), []);
  
  // Create initial nodes from the mock data
  const initialNodes = useMemo(() => {
    // Create schema boundaries to properly size and position schema background nodes
    const schemaBoundaries = {};
    
    // Initialize boundaries with schema positions
    mockApiData.schemas.forEach(schema => {
      schemaBoundaries[schema.slug] = {
        minX: schema.position.x,
        minY: schema.position.y,
        maxX: schema.position.x + schema.width,
        maxY: schema.position.y + schema.height
      };
    });
    
    // Update boundaries based on table positions
    mockApiData.tables.forEach(table => {
      const schemaSlug = table.schema;
      if (schemaBoundaries[schemaSlug]) {
        // Add some padding around tables (100px on each side)
        const tableMinX = table.orientation.x - 100;
        const tableMinY = table.orientation.y - 100;
        const tableMaxX = table.orientation.x + 280; // Table width + padding
        const tableMaxY = table.orientation.y + 280; // Table height + padding
        
        schemaBoundaries[schemaSlug].minX = Math.min(schemaBoundaries[schemaSlug].minX, tableMinX);
        schemaBoundaries[schemaSlug].minY = Math.min(schemaBoundaries[schemaSlug].minY, tableMinY);
        schemaBoundaries[schemaSlug].maxX = Math.max(schemaBoundaries[schemaSlug].maxX, tableMaxX);
        schemaBoundaries[schemaSlug].maxY = Math.max(schemaBoundaries[schemaSlug].maxY, tableMaxY);
      }
    });
    
    // Schema background nodes (add these first so they appear behind other nodes)
    const schemaBackgroundNodes = mockApiData.schemas.map(schema => {
      const bounds = schemaBoundaries[schema.slug];
      const width = bounds.maxX - bounds.minX;
      const height = bounds.maxY - bounds.minY;
      
      // Add 30% extra size
      const extraWidth = width * 0.3;
      const extraHeight = height * 0.3;
      
      return {
        id: `schema-bg-${schema.slug}`,
        type: 'schemaBackground',
        data: { 
          name: schema.name,
          color: schema.color,
          slug: schema.slug
        },
        position: { 
          x: bounds.minX - (extraWidth / 2),
          y: bounds.minY - (extraHeight / 2)
        },
        style: { 
          width: width + extraWidth,
          height: height + extraHeight,
          zIndex: -1 // Ensure it's behind other nodes
        },
        draggable: true,
        selectable: true,
      };
    });
    
    // Table nodes
    const tableNodes = mockApiData.tables.map(table => {
      return {
        id: table.slug,
        type: 'table',
        data: { 
          label: table.name,
          type: table.type,
          columns: table.columns
        },
        position: { 
          x: table.orientation.x, 
          y: table.orientation.y 
        },
        parentNode: `schema-bg-${table.schema}`,
        extent: 'parent',
      };
    });
    
    return [...schemaBackgroundNodes, ...tableNodes];
  }, []);
  
  // Create process nodes
  const processNodes = useMemo(() => {
    return mockApiData.processes.map(process => {
      // Find source and destination tables to calculate position
      const sourceTables = mockApiData.tables.filter(table => 
        process.source_table.includes(table.slug)
      );
      
      const destTables = mockApiData.tables.filter(table => 
        process.destination_table.includes(table.slug)
      );
      
      // Calculate average position between source and destination tables
      let x = 0, y = 0, count = 0;
      let parentSchema = null;
      
      sourceTables.forEach(table => {
        x += table.orientation.x;
        y += table.orientation.y;
        count++;
        // If all tables are in the same schema, the process should be in that schema too
        if (!parentSchema) parentSchema = table.schema;
        else if (parentSchema !== table.schema) parentSchema = null; // Mixed schemas
      });
      
      destTables.forEach(table => {
        x += table.orientation.x;
        y += table.orientation.y;
        count++;
        // If all tables are in the same schema, the process should be in that schema too
        if (!parentSchema) parentSchema = table.schema;
        else if (parentSchema !== table.schema) parentSchema = null; // Mixed schemas
      });
      
      if (count > 0) {
        x = x / count;
        y = y / count;
      }
      
      // Position the process node between source and destination
      // For source tables on the left and dest tables on the right
      if (sourceTables.length > 0 && destTables.length > 0) {
        const avgSourceX = sourceTables.reduce((sum, t) => sum + t.orientation.x, 0) / sourceTables.length;
        const avgDestX = destTables.reduce((sum, t) => sum + t.orientation.x, 0) / destTables.length;
        
        // If source is to the left of destination, place in between
        if (avgSourceX < avgDestX) {
          x = avgSourceX + (avgDestX - avgSourceX) * 0.5;
        }
        // If source is to the right of destination, place in between
        else {
          x = avgDestX + (avgSourceX - avgDestX) * 0.5;
        }
      }
      
      return {
        id: process.slug,
        type: 'process',
        data: { 
          label: process.name,
          description: process.description,
          type: process.type,
          status: process.status
        },
        position: { x, y },
        parentNode: parentSchema ? `schema-bg-${parentSchema}` : undefined,
        extent: parentSchema ? 'parent' : undefined,
      };
    });
  }, []);

  // Create edges between tables and processes
  const initialEdges = useMemo(() => {
    const edges = [];
    
    mockApiData.processes.forEach(process => {
      // Determine if process is active or inactive
      const isActive = process.status === 'active';
      
      // Set colors and animation based on process status
      const sourceColor = isActive ? '#52c41a' : '#aaaaaa'; // Green or gray
      const destColor = isActive ? '#1890ff' : '#aaaaaa';   // Blue or gray
      const animated = isActive; // Only animate if active
      const strokeWidth = isActive ? 2 : 1.5; // Slightly thinner if inactive
      
      // Get the mappings for this process
      const mappings = process.mappings || [];
      
      // Format source columns for display
      const sourceColumns = mappings.map(m => m.source);
      
      // Format target columns for display
      const targetColumns = mappings.map(m => m.target);
      
      // Create edges from source tables to process
      process.source_table.forEach(sourceId => {
        edges.push({
          id: `e-${sourceId}-${process.slug}`,
          source: sourceId,
          target: process.slug,
          type: 'custom',
          animated: animated,
          style: { 
            stroke: sourceColor, 
            strokeWidth: strokeWidth,
            opacity: isActive ? 1 : 0.7 // Slightly transparent if inactive
          },
          markerEnd: {
            type: 'arrowclosed',
            width: 20,
            height: 20,
            color: sourceColor,
          },
          data: {
            label: sourceColumns.join(', ') || 'No mappings',
            processName: process.name,
            isActive: isActive, // Pass status to the edge component
            edgeType: 'source',
            mappings: sourceColumns
          }
        });
      });
      
      // Create edges from process to destination tables
      process.destination_table.forEach(destId => {
        edges.push({
          id: `e-${process.slug}-${destId}`,
          source: process.slug,
          target: destId,
          type: 'custom',
          animated: animated,
          style: { 
            stroke: destColor, 
            strokeWidth: strokeWidth,
            opacity: isActive ? 1 : 0.7 // Slightly transparent if inactive
          },
          markerEnd: {
            type: 'arrowclosed',
            width: 20,
            height: 20,
            color: destColor,
          },
          data: {
            label: targetColumns.join(', ') || 'No mappings',
            processName: process.name,
            isActive: isActive, // Pass status to the edge component
            edgeType: 'target',
            mappings: targetColumns
          }
        });
      });
    });
    
    return edges;
  }, []);

  // Use React Flow's node state management
  const [nodes, setNodes, onNodesChangeDefault] = useNodesState([...initialNodes, ...processNodes]);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  
  // Custom nodes change handler to update schema boundaries when nodes move
  const onNodesChange = useCallback(
    (changes) => {
      // Apply the default node changes
      onNodesChangeDefault(changes);
      
      // Check if any of the changes are position changes
      const hasPositionChanges = changes.some(
        change => change.type === 'position' && change.dragging
      );
      
      // If there are position changes, we might want to update schema boundaries
      // This is a placeholder for more complex logic if needed
      if (hasPositionChanges) {
        // console.log('Nodes are being moved');
      }
    },
    [onNodesChangeDefault]
  );
  
  // Handle connecting nodes
  const onConnect = useCallback(
    (params) => {
      // Only allow connections from tables to processes or processes to tables
      const sourceNode = nodes.find(node => node.id === params.source);
      const targetNode = nodes.find(node => node.id === params.target);
      
      if (!sourceNode || !targetNode) return;
      
      // Check if connection is valid (table -> process or process -> table)
      const isSourceTable = sourceNode.type === 'table';
      const isTargetProcess = targetNode.type === 'process';
      const isSourceProcess = sourceNode.type === 'process';
      const isTargetTable = targetNode.type === 'table';
      
      if ((isSourceTable && isTargetProcess) || (isSourceProcess && isTargetTable)) {
        // Create a new edge with custom styling
        const newEdge = {
          ...params,
          type: 'custom',
          animated: true,
          style: { 
            stroke: isSourceTable ? '#52c41a' : '#1890ff', 
            strokeWidth: 2 
          },
          markerEnd: {
            type: 'arrowclosed',
            width: 20,
            height: 20,
            color: isSourceTable ? '#52c41a' : '#1890ff',
          },
          data: {
            label: 'New Connection',
            isActive: true
          }
        };
        
        setEdges(eds => addEdge(newEdge, eds));
        
        // TODO: Open a form to configure the connection
        // This would be where you define mappings, transformations, etc.
      }
    },
    [nodes, setEdges]
  );
  
  // Handle node click
  const onNodeClick = useCallback((event, node) => {
    // Prevent clicks on schema background nodes from opening forms
    if (node.type === 'schemaBackground') return;
    
    if (node.type === 'process') {
      // Find the process in the mock data
      const process = mockApiData.processes.find(p => p.slug === node.id);
      if (process) {
        setSelectedProcessForEdit(process);
        setShowProcessForm(true);
      }
    } else if (node.type === 'table') {
      // Find the table in the mock data
      const table = mockApiData.tables.find(t => t.slug === node.id);
      if (table) {
        setSelectedTableForEdit(table);
        setShowTableForm(true);
      }
    }
  }, []);
  
  // Add a new table
  const addTableNode = () => {
    const newTable = {
      id: `table-${Date.now()}`,
      type: 'table',
      data: { 
        label: 'New Table',
        type: 'dimension',
        columns: ['id', 'name', 'description']
      },
      position: { 
        x: Math.random() * 500, 
        y: Math.random() * 500 
      },
    };
    
    setNodes(nodes => [...nodes, newTable]);
  };

  // Get canvas boundaries for process placement
  const getSchemasBoundaries = () => {
    // Calculate based on existing nodes
    let minX = 100;
    let minY = 100;
    let maxX = 500;
    let maxY = 500;
    
    if (nodes.length > 0) {
      minX = Math.min(...nodes.map(node => node.position.x));
      minY = Math.min(...nodes.map(node => node.position.y));
      maxX = Math.max(...nodes.map(node => node.position.x + 200)); // Assuming node width is ~200px
      maxY = Math.max(...nodes.map(node => node.position.y + 200)); // Assuming node height is ~200px
    }
    
    return { minX, minY, maxX, maxY };
  };
  
  // We don't need the moveSchemaNodes function anymore
  
  // Open the process form to create a new process
  const addProcessNode = () => {
    setSelectedProcessForEdit(null);
    setShowProcessForm(true);
  };
  
  // Handle saving a new process from the form
  const handleSaveProcess = (processData) => {
    const { minX, minY, maxX, maxY } = getSchemasBoundaries();
    
    // Place the new process outside of all schemas
    // Either to the right of all schemas or below all schemas
    let newX, newY;
    
    // Get the current viewport to help position the new node in visible area
    const viewport = reactFlowInstance ? reactFlowInstance.getViewport() : { x: 0, y: 0, zoom: 1 };
    
    // Determine placement position - right side is preferred
    newX = maxX + 200;
    newY = minY + (maxY - minY) / 2;
    
    // If we're editing an existing process, find it and update it
    if (selectedProcessForEdit) {
      const updatedNodes = nodes.map(node => {
        if (node.id === selectedProcessForEdit.slug) {
          return {
            ...node,
            data: {
              ...node.data,
              label: processData.name,
              description: processData.description,
              type: processData.type,
              status: processData.status,
              mappings: processData.mappings,
              filters: processData.filters,
              aggregations: processData.aggregations
            }
          };
        }
        return node;
      });
      
      setNodes(updatedNodes);
      
      // Update edges if source or destination tables have changed
      const oldProcess = mockApiData.processes.find(p => p.slug === selectedProcessForEdit.slug);
      
      // Remove old edges
      if (oldProcess) {
        const edgesToRemove = edges.filter(edge => 
          edge.source === oldProcess.slug || 
          edge.target === oldProcess.slug
        );
        
        if (edgesToRemove.length > 0) {
          const remainingEdges = edges.filter(edge => 
            !edgesToRemove.some(e => e.id === edge.id)
          );
          
          setEdges(remainingEdges);
        }
      }
      
      // Create new edges
      const newEdges = [];
      
      // Determine if process is active or inactive
      const isActive = processData.status === 'active';
      
      // Set colors and animation based on process status
      const sourceColor = isActive ? '#52c41a' : '#aaaaaa'; // Green or gray
      const destColor = isActive ? '#1890ff' : '#aaaaaa';   // Blue or gray
      const animated = isActive; // Only animate if active
      const strokeWidth = isActive ? 2 : 1.5; // Slightly thinner if inactive
      const opacity = isActive ? 1 : 0.7; // Slightly transparent if inactive
      
      // Get the mappings for this process
      const mappings = processData.mappings || [];
      
      // Format source columns for display
      const sourceColumns = mappings.map(m => m.source);
      
      // Format target columns for display
      const targetColumns = mappings.map(m => m.target);
      
      // Create edges from source tables to process
      processData.source_table.forEach(sourceId => {
        newEdges.push({
          id: `e-${sourceId}-${processData.slug}`,
          source: sourceId,
          target: processData.slug,
          type: 'custom',
          animated: animated,
          style: { 
            stroke: sourceColor, 
            strokeWidth: strokeWidth,
            opacity: opacity
          },
          markerEnd: {
            type: 'arrowclosed',
            width: 20,
            height: 20,
            color: sourceColor,
          },
          data: {
            label: sourceColumns.join(', ') || 'No mappings',
            processName: processData.name,
            isActive: isActive,
            edgeType: 'source',
            mappings: sourceColumns
          }
        });
      });
      
      // Create edges from process to destination tables
      processData.destination_table.forEach(destId => {
        newEdges.push({
          id: `e-${processData.slug}-${destId}`,
          source: processData.slug,
          target: destId,
          type: 'custom',
          animated: animated,
          style: { 
            stroke: destColor, 
            strokeWidth: strokeWidth,
            opacity: opacity
          },
          markerEnd: {
            type: 'arrowclosed',
            width: 20,
            height: 20,
            color: destColor,
          },
          data: {
            label: targetColumns.join(', ') || 'No mappings',
            processName: processData.name,
            isActive: isActive,
            edgeType: 'target',
            mappings: targetColumns
          }
        });
      });
      
      setEdges(eds => [...eds, ...newEdges]);
      
      // Update the mock data
      const processIndex = mockApiData.processes.findIndex(p => p.slug === processData.slug);
      if (processIndex !== -1) {
        mockApiData.processes[processIndex] = processData;
      } else {
        mockApiData.processes.push(processData);
      }
    } else {
      // Create a new process node
      const newProcess = {
        id: processData.slug,
        type: 'process',
        data: { 
          label: processData.name,
          description: processData.description,
          type: processData.type,
          status: processData.status,
          mappings: processData.mappings,
          filters: processData.filters,
          aggregations: processData.aggregations
        },
        position: { x: newX, y: newY }
      };
      
      setNodes(nodes => [...nodes, newProcess]);
      
      // Create edges for the new process
      const newEdges = [];
      
      // Determine if process is active or inactive
      const isActive = processData.status === 'active';
      
      // Set colors and animation based on process status
      const sourceColor = isActive ? '#52c41a' : '#aaaaaa'; // Green or gray
      const destColor = isActive ? '#1890ff' : '#aaaaaa';   // Blue or gray
      const animated = isActive; // Only animate if active
      const strokeWidth = isActive ? 2 : 1.5; // Slightly thinner if inactive
      
      // Get the mappings for this process
      const mappings = processData.mappings || [];
      
      // Format source columns for display
      const sourceColumns = mappings.map(m => m.source);
      
      // Format target columns for display
      const targetColumns = mappings.map(m => m.target);
      
      // Create edges from source tables to process
      processData.source_table.forEach(sourceId => {
        newEdges.push({
          id: `e-${sourceId}-${processData.slug}`,
          source: sourceId,
          target: processData.slug,
          type: 'custom',
          animated: animated,
          style: { 
            stroke: sourceColor, 
            strokeWidth: strokeWidth,
            opacity: isActive ? 1 : 0.7
          },
          markerEnd: {
            type: 'arrowclosed',
            width: 20,
            height: 20,
            color: sourceColor,
          },
          data: {
            label: sourceColumns.join(', ') || 'No mappings',
            processName: processData.name,
            isActive: isActive,
            edgeType: 'source',
            mappings: sourceColumns
          }
        });
      });
      
      // Create edges from process to destination tables
      processData.destination_table.forEach(destId => {
        newEdges.push({
          id: `e-${processData.slug}-${destId}`,
          source: processData.slug,
          target: destId,
          type: 'custom',
          animated: animated,
          style: { 
            stroke: destColor, 
            strokeWidth: strokeWidth,
            opacity: isActive ? 1 : 0.7
          },
          markerEnd: {
            type: 'arrowclosed',
            width: 20,
            height: 20,
            color: destColor,
          },
          data: {
            label: targetColumns.join(', ') || 'No mappings',
            processName: processData.name,
            isActive: isActive,
            edgeType: 'target',
            mappings: targetColumns
          }
        });
      });
      
      setEdges(eds => [...eds, ...newEdges]);
      
      // Add to mock data
      mockApiData.processes.push(processData);
    }
  };
  
  // Add custom CSS to the document
  useEffect(() => {
    const styleElement = document.createElement('style');
    styleElement.textContent = generateCustomStyles();
    document.head.appendChild(styleElement);
    
    return () => {
      document.head.removeChild(styleElement);
    };
  }, []);
  
  // Set initial viewport
  useEffect(() => {
    if (reactFlowInstance) {
      reactFlowInstance.setViewport(mockApiData.viewportSettings);
    }
  }, [reactFlowInstance]);
  
  // Set up a global callback for process editing
  // This is used by the ProcessNode component
  useEffect(() => {
    window.processEditCallback = (process) => {
      setSelectedProcessForEdit(process);
      setShowProcessForm(true);
    };
    
    return () => {
      // Clean up when component unmounts
      window.processEditCallback = null;
    };
  }, []);
  
  return (
    <div style={{ width: '100%', height: '100vh' }}>
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        onNodeClick={onNodeClick}
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
        fitView
        attributionPosition="bottom-left"
      >
        <Controls />
        <MiniMap 
          nodeStrokeWidth={3}
          zoomable
          pannable
        />
        <Background color="#f0f0f0" gap={16} />
        
        <Panel position="top-right" style={{ 
          display: 'flex', 
          gap: '10px',
          background: 'white',
          padding: '10px',
          borderRadius: '5px',
          boxShadow: '0 0 10px rgba(0,0,0,0.1)'
        }}>
          <button 
            onClick={addProcessNode}
            style={{
              display: 'flex',
              alignItems: 'center',
              gap: '5px',
              background: '#fa8c16',
              color: 'white',
              border: 'none',
              padding: '8px 12px',
              borderRadius: '4px',
              cursor: 'pointer',
              fontWeight: 'bold',
              boxShadow: '0 2px 5px rgba(0,0,0,0.1)'
            }}
          >
            <CustomProcessIcon width="18" height="18" /> Add Process
          </button>
          <button 
            onClick={addTableNode}
            style={{
              display: 'flex',
              alignItems: 'center',
              gap: '5px',
              background: '#1890ff',
              color: 'white',
              border: 'none',
              padding: '8px 12px',
              borderRadius: '4px',
              cursor: 'pointer',
              fontWeight: 'bold',
              boxShadow: '0 2px 5px rgba(0,0,0,0.1)'
            }}
          >
            <FaTable /> Add Table
          </button>
        </Panel>
      </ReactFlow>
      
      {/* Process Form */}
      <ProcessForm 
        isOpen={showProcessForm}
        onClose={() => setShowProcessForm(false)}
        onSave={handleSaveProcess}
        tables={mockApiData.tables}
        existingProcess={selectedProcessForEdit}
      />
    </div>
  );
};

// Wrap with ReactFlowProvider
const DataflowCanvasWithProvider = () => (
  <ReactFlowProvider>
    <DataflowCanvasUpdated />
  </ReactFlowProvider>
);

export default DataflowCanvasWithProvider;