import React, { useState, useRef, useCallback, useEffect } from 'react';
import GraphToolbar from './graphComponents/GraphToolbar';
import GraphCanvas from './graphComponents/graphCanvas';
import PropertyInspector from './graphComponents/PropertyInspector';
import ChatInterface from '../ChatInterface';
import { Neo4jGraphData } from './graphUtils/loadGraphData';
import neo4j from 'neo4j-driver';
import { BarChart3, Database, RefreshCw } from 'lucide-react';
import GraphGov from './graphComponents/graphGov';

interface CreateNodeData {
  labels: string[];
  properties: Record<string, any>;
}

// TypeScript interfaces for Neo4j data structures
interface Neo4jNode {
  id: string;
  labels: string[];
  properties: Record<string, any>;
}

interface Neo4jRelationship {
  id: string;
  type: string;
  startNodeId: string;
  endNodeId: string;
  properties: Record<string, any>;
}

// D3 force simulation types
interface D3Node extends Neo4jNode {
  x?: number;
  y?: number;
  fx?: number | null;
  fy?: number | null;
  vx?: number;
  vy?: number;
}

interface D3Link extends Neo4jRelationship {
  source: D3Node | string;
  target: D3Node | string;
}

// Neo4j Connection Configuration
interface Neo4jConfig {
  uri: string;
  username: string;
  password: string;
  database?: string;
}

// Types
interface GraphConfig {
  width: number;
  height: number;
  nodeRadius: number;
  linkDistance: number;
  chargeStrength: number;
  collisionRadius: number;
}

const defaultGraphConfig: GraphConfig = {
  width: 1000,
  height: 700,
  nodeRadius: 25,
  linkDistance: 120,
  chargeStrength: -400,
  collisionRadius: 35,
};

const neo4jColorScheme = {
  nodeColors: {
    Person: '#4C8EDA',
    Company: '#5DBB63',
    Project: '#FFB74D',
    City: '#9C27B0',
    Technology: '#FF5722',
    Department: '#607D8B',
    Family: '#E91E63',
    Control: '#2196F3',
    Enhancement: '#FF9800',
    default: '#008CC1',
  },
};

// Utility functions
const getNodeColor = (labels: string[]): string => {
  const primaryLabel = labels[0];
  return neo4jColorScheme.nodeColors[primaryLabel as keyof typeof neo4jColorScheme.nodeColors] 
    || neo4jColorScheme.nodeColors.default;
};

const getNodeDisplayName = (node: any): string => {
  return node.properties.name 
    || node.properties.title 
    || node.properties.code
    || node.properties.id 
    || node.labels[0] 
    || node.id;
};

const formatPropertyValue = (value: any): string => {
  if (value === null || value === undefined) return '';
  if (typeof value === 'object') return JSON.stringify(value);
  if (typeof value === 'boolean') return value ? 'true' : 'false';
  return String(value);
};

const truncateText = (text: string, maxLength: number = 20): string => {
  if (text.length <= maxLength) return text;
  return text.substring(0, maxLength - 3) + '...';
};

const getNodeStats = (graphData: Neo4jGraphData) => {
  const labelCounts = graphData.nodes.reduce((acc, node) => {
    node.labels.forEach(label => {
      acc[label] = (acc[label] || 0) + 1;
    });
    return acc;
  }, {} as Record<string, number>);

  const relationshipCounts = graphData.relationships.reduce((acc, rel) => {
    acc[rel.type] = (acc[rel.type] || 0) + 1;
    return acc;
  }, {} as Record<string, number>);

  return {
    totalNodes: graphData.nodes.length,
    totalRelationships: graphData.relationships.length,
    labelCounts,
    relationshipCounts,
  };
};

// Main Component
const GraphVisualizer2: React.FC = () => {
  const [selectedNode, setSelectedNode] = useState<Neo4jNode | null>(null);
  const [selectedRelationship, setSelectedRelationship] = useState<Neo4jRelationship | null>(null);
  const [isAddingNode, setIsAddingNode] = useState(false);
  const [nextNodeId, setNextNodeId] = useState(4);
  const [defaultGraphData, setDefaultGraphData] = useState<Neo4jGraphData>({
    nodes: [],
    relationships: [],
  });
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [isConnected, setIsConnected] = useState(false);

  // Neo4j Configuration - you might want to move this to environment variables
  const [neo4jConfig, setNeo4jConfig] = useState<Neo4jConfig>({
    uri: 'bolt://localhost:7687',
    username: 'neo4j',
    password: 'password',
    database: 'neo4j'
  });

  // Enhanced Neo4j data loading function
  const loadNeo4jData = useCallback(async (
    query: string, 
    config: Neo4jConfig = neo4jConfig
  ): Promise<Neo4jGraphData> => {
    const driver = neo4j.driver(config.uri, neo4j.auth.basic(config.username, config.password));
    let session;

    try {
      // Use specific database if provided
      session = config.database 
        ? driver.session({ database: config.database })
        : driver.session();

      console.log('Executing Neo4j query:', query);
      const result = await session.run(query);
      console.log('Query returned', result.records.length, 'records');

      const nodes: Neo4jGraphData['nodes'] = [];
      const relationships: Neo4jGraphData['relationships'] = [];
      const nodeIds = new Set<string>();
      const relationshipIds = new Set<string>();

      result.records.forEach((record, recordIndex) => {
        // Process all fields in the record
        record.keys.forEach(key => {
          const value = record.get(key);
          
          if (value && value.labels) {
            // This is a node
            const nodeId = value.identity.toString();
            if (!nodeIds.has(nodeId)) {
              nodes.push({
                id: nodeId,
                labels: value.labels,
                properties: { ...value.properties },
              });
              nodeIds.add(nodeId);
            }
          } else if (value && value.type) {
            // This is a relationship
            const relId = value.identity.toString();
            if (!relationshipIds.has(relId)) {
              relationships.push({
                id: relId,
                type: value.type,
                startNodeId: value.start.toString(),
                endNodeId: value.end.toString(),
                properties: { ...value.properties },
              });
              relationshipIds.add(relId);
            }
          }
        });
      });

      // Handle path results (common in graph queries)
      result.records.forEach(record => {
        record.keys.forEach(key => {
          const value = record.get(key);
          if (value && value.segments) {
            // This is a path
            value.segments.forEach((segment: any) => {
              // Add start node
              const startId = segment.start.identity.toString();
              if (!nodeIds.has(startId)) {
                nodes.push({
                  id: startId,
                  labels: segment.start.labels,
                  properties: { ...segment.start.properties },
                });
                nodeIds.add(startId);
              }

              // Add end node
              const endId = segment.end.identity.toString();
              if (!nodeIds.has(endId)) {
                nodes.push({
                  id: endId,
                  labels: segment.end.labels,
                  properties: { ...segment.end.properties },
                });
                nodeIds.add(endId);
              }

              // Add relationship
              const relId = segment.relationship.identity.toString();
              if (!relationshipIds.has(relId)) {
                relationships.push({
                  id: relId,
                  type: segment.relationship.type,
                  startNodeId: segment.relationship.start.toString(),
                  endNodeId: segment.relationship.end.toString(),
                  properties: { ...segment.relationship.properties },
                });
                relationshipIds.add(relId);
              }
            });
          }
        });
      });

      console.log('Processed nodes:', nodes.length);
      console.log('Processed relationships:', relationships.length);

      return { nodes, relationships };
    } catch (error) {
      console.error('Error fetching Neo4j data:', error);
      throw error;
    } finally {
      if (session) {
        await session.close();
      }
      await driver.close();
    }
  }, [neo4jConfig]);

  // Test Neo4j connection
  const testConnection = useCallback(async () => {
    setIsLoading(true);
    setError(null);

    try {
      const driver = neo4j.driver(neo4jConfig.uri, neo4j.auth.basic(neo4jConfig.username, neo4jConfig.password));
      const session = driver.session();
      
      // Simple test query
      await session.run('RETURN 1');
      
      await session.close();
      await driver.close();
      
      setIsConnected(true);
      console.log('Neo4j connection successful');
    } catch (err) {
      setIsConnected(false);
      setError(err instanceof Error ? err.message : 'Connection failed');
      console.error('Neo4j connection failed:', err);
    } finally {
      setIsLoading(false);
    }
  }, [neo4jConfig]);

  // Load sample data or specific query
  const loadGraphData = useCallback(async (customQuery?: string) => {
    setIsLoading(true);
    setError(null);

    try {
      // Default query - you can customize this based on your data structure
      const defaultQuery = customQuery || `
        MATCH path = (rc:RiskCategory {name: "Operation Risk"})-[:HAS_SOURCE]->(s:Source)-[:HAS_RISK|HAS_ASSET|HAS_ASSET_CATEGORY]->(connected)
RETURN path
      `;

      const graphData = await loadNeo4jData(defaultQuery);
      setDefaultGraphData(graphData);
      
      if (graphData.nodes.length > 0) {
        setNextNodeId(Math.max(...graphData.nodes.map(n => parseInt(n.id))) + 1);
      }

      console.log('Graph data loaded successfully');
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to load graph data');
      console.error('Error loading graph data:', err);
    } finally {
      setIsLoading(false);
    }
  }, [loadNeo4jData]);

  // Load your specific Family/Control/Enhancement data
  const loadFamilyControlData = useCallback(async () => {
    const query = `
      MATCH (f:Family)
      OPTIONAL MATCH (f)-[:HAS_CONTROL]->(c:Control)
      OPTIONAL MATCH (c)-[:HAS_ENHANCEMENT]->(e:Enhancement)
      RETURN f, c, e
      LIMIT 100
    `;
    
    await loadGraphData(query);
  }, [loadGraphData]);

  // Event handlers
  const handleNodeClick = (node: Neo4jNode) => {
    setSelectedNode(node);
    setSelectedRelationship(null);
  };

  const handleRelationshipClick = (relationship: Neo4jRelationship) => {
    setSelectedRelationship(relationship);
    setSelectedNode(null);
  };

  const handleClearSelection = () => {
    setSelectedNode(null);
    setSelectedRelationship(null);
  };

  const handleRefresh = () => {
    loadGraphData();
    handleClearSelection();
  };

  const handleNodeDoubleClick = useCallback((node: Neo4jNode) => {
    console.log('Double clicked node:', node);
  }, []);

  const handleAddNode = useCallback((position: { x: number; y: number }) => {
    console.log('Add node at position:', position);
  }, []);

  const handleToggleAddNode = useCallback(() => {
    setIsAddingNode(prev => !prev);
    if (isAddingNode) {
      handleClearSelection();
    }
  }, [isAddingNode, handleClearSelection]);

  const handleDeleteNode = useCallback((nodeId: string) => {
    setDefaultGraphData(prev => ({
      nodes: prev.nodes.filter(node => node.id !== nodeId),
      relationships: prev.relationships.filter(
        rel => rel.startNodeId !== nodeId && rel.endNodeId !== nodeId
      )
    }));
    handleClearSelection();
  }, [handleClearSelection]);
  
  const handleUpdateNode = useCallback((nodeId: string, updates: Partial<Neo4jNode>) => {
    setDefaultGraphData(prev => ({
      ...prev,
      nodes: prev.nodes.map(node => 
        node.id === nodeId ? { ...node, ...updates } : node
      )
    }));
  }, []);

  const handleCreateNode = useCallback((nodeData: CreateNodeData) => {
    const newNode: Neo4jNode = {
      id: nextNodeId.toString(),
      labels: nodeData.labels,
      properties: nodeData.properties
    };

    setDefaultGraphData(prev => ({
      ...prev,
      nodes: [...prev.nodes, newNode]
    }));

    setNextNodeId(prev => prev + 1);
    setIsAddingNode(false);
    setSelectedNode(newNode);
  }, [nextNodeId]);

  // Initialize connection test on component mount
  useEffect(() => {
    testConnection();
  }, [testConnection]);

  return (
    <div className="h-screen bg-gray-100 flex flex-col">
      {/* Toolbar */}
      <GraphToolbar 
        graphData={defaultGraphData}
        onRefresh={handleRefresh}
      />

      {/* Main Content */}
      <div className="flex-1 flex overflow-hidden">
        {/* Graph Canvas */}
        <div className="flex-1 p-4">
          <div className="h-[70%] bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden relative">
            {isLoading && (
              <div className="absolute inset-0 bg-white bg-opacity-75 flex items-center justify-center z-10">
                <div className="flex items-center gap-2">
                  <RefreshCw className="w-5 h-5 animate-spin text-blue-600" />
                  <span className="text-blue-600">Loading graph data...</span>
                </div>
              </div>
            )}
            
            <GraphCanvas
              graphData={defaultGraphData}
              selectedNode={selectedNode}
              selectedRelationship={selectedRelationship}
              onNodeClick={handleNodeClick}
              onRelationshipClick={handleRelationshipClick}
              onNodeDoubleClick={handleNodeDoubleClick}
              onAddNode={handleAddNode}
              isAddingNode={isAddingNode}
              config={{
                width: 800,
                height: 600,
                nodeRadius: 25,
                linkDistance: 150,
                chargeStrength: -300
              }}
            />
          </div>
          
          <div className="h-[29%] bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden my-2">
            <ChatInterface/>
          </div>
        </div>

        {/* Property Inspector */}
        <div className="flex h-[97%] mt-4 rounded-lg mr-4">
          <PropertyInspector
            selectedNode={selectedNode}
            selectedRelationship={selectedRelationship}
            onClearSelection={handleClearSelection}
            onToggleAddNode={handleToggleAddNode}
            isAddingNode={isAddingNode}
            onCreateNode={handleCreateNode}
            onDeleteNode={handleDeleteNode}
            onUpdateNode={handleUpdateNode}
          />
        </div>
        <GraphGov onITRMClick={loadGraphData}/>
      </div>

      {/* Status Bar */}
      <div className="bg-white border-t border-gray-200 px-6 py-2">
        <div className="flex items-center justify-between text-sm text-gray-500">
          <div className="flex items-center gap-4">
            
            <span className={isConnected ? 'text-green-600' : 'text-red-600'}>
            Memory {isConnected ? 'Connected' : 'Disconnected'}
            </span>
            <span>•</span>
            <button
              onClick={testConnection}
              disabled={isLoading}
              className="flex items-center gap-1 px-3 py-1 text-sm bg-blue-50 hover:bg-blue-100 text-blue-700 rounded-md transition-colors disabled:opacity-50"
            >
              <Database className="w-4 h-4" />
              Test Connection
            </button>
            {selectedNode && (
              <>
                <span>•</span>
                <span className="text-blue-600 font-medium">
                  Node "{getNodeDisplayName(selectedNode)}" selected
                </span>
              </>
            )}
            {selectedRelationship && (
              <>
                <span>•</span>
                <span className="text-green-600 font-medium">
                  Relationship "{selectedRelationship.type}" selected
                </span>
              </>
            )}
          </div>
          <div className="flex items-center gap-4">
            <span>Performance: {isLoading ? 'Loading...' : 'Excellent'}</span>
            <span>•</span>
            <span>Memory: 45.2 MB</span>
          </div>
        </div>
      </div>
    </div>
  );
};

export default GraphVisualizer2;