import { useState, useCallback, ReactNode } from 'react';
import {
  ReactFlow,
  applyNodeChanges,
  applyEdgeChanges,
  addEdge,
  Background,
  Controls,
  ReactFlowProvider,
  NodeTypes,
  NodeChange,
  EdgeChange,
  MarkerType,
  NodeMouseHandler,
  OnConnect,
  MiniMap,
  IsValidConnection,
  EdgeMouseHandler
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { AppNode, AppEdge, NodeData, EdgeData, Settings } from './types/graph';
import CustomNode from './components/CustomNode';
import NodeEditor from './components/NodeEditor';
import EdgeEditor from './components/EdgeEditor'
import Toggle from "./components/Toggle"
import { generateWireGuardPrivateKey } from './utils/wireguardConfig';
import './App.css';

const initialNodes: AppNode[] = [];
const initialEdges: AppEdge[] = [];
const initialSettings : Settings = {
  listenPort: 38894,
  mtu: 1420,
};

const nodeTypes : NodeTypes = {
  custom: CustomNode,
};

function generateNodeData(count: number) : NodeData | null {
  const privateKey = generateWireGuardPrivateKey();
  const node : NodeData = {
    id: `n-${crypto.randomUUID()}`,
    label: `Node-${count + 1}`,
    privateKey: privateKey
  }
  return node
}

function FlowContent(): ReactNode {
  const [nodes, setNodes] = useState<AppNode[]>(initialNodes);
  const [edges, setEdges] = useState<AppEdge[]>(initialEdges);
  const [settings, setSettings] = useState<Settings>(initialSettings);

  const [editingNode, setEditingNode] = useState<NodeData | undefined>(undefined);
  const [editingEdge, setEditingEdge] = useState<EdgeData | undefined>(undefined);
  const [enableTwoWay, setEnableTwoWay] = useState(false);

  const onNodesChange = useCallback(
    (changes: NodeChange<AppNode>[]) => setNodes((nds) => applyNodeChanges<AppNode>(changes, nds)),
    []);

  const onEdgesChange = useCallback(
    (changes : EdgeChange<AppEdge>[]) => setEdges((eds) => applyEdgeChanges<AppEdge>(changes, eds)),
    []);

  const onConnect = useCallback<OnConnect>(
    (params) => {
      const id = `e-${crypto.randomUUID()}`
      const newEdge : AppEdge = {
        ...params,
        id: id,
        animated: !enableTwoWay,
        markerEnd: enableTwoWay ? undefined : { type: MarkerType.ArrowClosed },
        data : {
          id: id,
          isTwoWayEdge: enableTwoWay
        }
      }
      return setEdges((eds) => addEdge<AppEdge>(newEdge, eds));
    },
    [enableTwoWay]);

  const onNodeClick = useCallback<NodeMouseHandler<AppNode>>(
    (_event, node) => setEditingNode(node.data), 
    []);

  const onEdgeClick = useCallback<EdgeMouseHandler<AppEdge>>(
    (_event, edge) => setEditingEdge(edge.data),
    []
  )

  const validateConnection = useCallback<IsValidConnection>(
    (connection) => {
      if (connection.source === connection.target) {
        return false;
      }
      const isDuplicate = edges.some(
        (edge) => (
          (edge.source === connection.source && edge.target === connection.target) ||
          (edge.source === connection.target && edge.target === connection.source)
        )
      );
      return !isDuplicate;
    },
    [edges]);

  const handleAddNode = (): void => {
    const result = generateNodeData(nodes.length);
    if(result == null) return;
    const newNode: AppNode = {
      id: result.id,
      position: { x: 0, y: 0 },
      data: result,
      type: 'custom',
      selected: true
    };
    setNodes((prev) => [...prev.map(node => ({ ...node, selected: false })), newNode]);
  };

  const handleUpdateNode = (updatedData: NodeData): void => {
    setNodes((prev) =>
      prev.map((node) => {
        if (node.data.id === editingNode?.id) {
          return { ...node, data: updatedData };
        }
        return node;
      })
    );
    setEditingNode(undefined);
  };

  const handleUpdateEdge = (updatedData: EdgeData): void => {
    setEdges((prev) =>
      prev.map((edge) => {
        if (edge.data && edge.data.id === editingEdge?.id) {
          return { ...edge, data: updatedData };
        }
        return edge;
      })
    );
    setEditingNode(undefined);
  };

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        onNodeDoubleClick={onNodeClick}
        onEdgeDoubleClick={onEdgeClick}
        nodeTypes={nodeTypes}
        deleteKeyCode={["Delete"]}
        fitView
        isValidConnection={validateConnection}
      >
        <Background />
        <Controls />
        <MiniMap 
        nodeColor={(n) => {
          if (n.type === 'input') return 'blue';
          return '#eee';
        }} 
        maskColor="rgba(0, 0, 0, 0.1)"
        position="bottom-right" // 也可以是 top-right 等
      />
      </ReactFlow>

      <div className="toolbar">
        <div className="toolbar-group">
          <Toggle 
            className="toolbar-item" 
            checked = {enableTwoWay}
            onChange={checked => setEnableTwoWay(checked)} >双向连接</Toggle>

          <button className="toolbar-btn" onClick={handleAddNode} title="添加新节点">
            ➕ 添加节点
          </button>

          <button className="toolbar-btn" onClick={() => {}} title="设置">
            📋 设置
          </button>

        </div>
      </div>

      {editingNode && (
        <NodeEditor
          node={editingNode}
          onUpdate={handleUpdateNode}
          onClose={() => setEditingNode(undefined)}
          settings={settings}
        />
      )}

      {editingEdge && (
        <EdgeEditor
          edge={editingEdge}
          onUpdate={handleUpdateEdge}
          onClose={() => setEditingEdge(undefined)}
        />
      )}
      
    </div>
  );
}

export default function App(): ReactNode {
  return (
    <ReactFlowProvider>
      <FlowContent />
    </ReactFlowProvider>
  );
}
