// 自定义批算子时使用的流程图

import ReactFlow, {
	Controls,
	Background,
	useNodesState,
	useEdgesState,
	ReactFlowProps,
	Node,
	Connection,
	Edge,
} from "react-flow-renderer";
import { useCallback, useEffect, useMemo, useState } from "react";

import { uuid } from "@/utils/util";
import { IParameter, ITask } from "../../../../ProjectSubmitWork/interface";
import { ILine } from "../../interface";
import BatchNode from "../BatchNode";
import FlowNode from "../FlowNode";
import { getCustomTemplateParameterCheckResult } from "@/views/WorkFlowEdit/util";
import { useMessage } from "@/components/MySnackbar";
import styles from "./index.module.css";

interface IProps extends ReactFlowProps {
	tasks?: ITask[];
	/** 类型， edit为编辑类型 */
	type?: "edit" | "default";
	/** 设置组件数据 组件为编辑状态使用 */
	setTasks?: (val: ITask[]) => void;
	/** 点击流程node 节点 返回唯一标识符 */
	onFlowNodeClick?: (val: string) => void;
	/** 监听事件的状态 */
	ListenState?: boolean;
	/** 流节点是否可以拖拽 */
	flowNodeDraggable?: boolean;
	// 是否显示Controls（放大缩小全屏等按钮）
	showControls?: boolean;
	showVersion?: boolean; // 在流程图中是否显示算子版本
}

const BatchOperatorFlow = (props: IProps) => {
	const {
		tasks,
		type: flowType = "default",
		setTasks,
		onFlowNodeClick,
		ListenState = true,
		flowNodeDraggable = false,
		showControls = true,
		showVersion = false,
		...other
	} = props;
	console.log(tasks);
	/** 自定义的节点类型 */
	const nodeTypes = useMemo(() => {
		return { batchNode: BatchNode, flowNode: FlowNode };
	}, []);
	/** 内部维护的选择的flow节点Id */
	const [inSideFlowNodeId, setInSideFlowNodeId] = useState<string>("");
	/** 选中的线 */
	const [selectedEdge, setSelectedEdge] = useState<Edge>();
	const Message = useMessage();

	/** 原始数据删除线 */
	const tasksDeleteLine = useCallback(
		(connection: Connection | Edge | any) => {
			const result =
				(tasks?.length &&
					tasks.map((item) => {
						// /** 删除batch起始的edges中的一项 === 等于删除了一根连线 */
						if (item.id === connection.source) {
							const newEdges =
								(item.edges?.length &&
									item.edges?.filter((every) => every.id !== connection.id)) ||
								[];
							return {
								...item,
								edges: newEdges,
							};
						} else {
							return item;
						}
					})) ||
				[];
			return result;
		},
		[tasks]
	);

	/** 删除流节点或者线 */
	const deleteSelectFlowNode = useCallback(
		(e: any) => {
			if (e.keyCode === 8 && ListenState) {
				/** 删除流节点逻辑 */
				if (inSideFlowNodeId) {
					const newVal =
						(tasks?.length &&
							tasks.filter((item) => {
								return item.id !== inSideFlowNodeId;
							})) ||
						[];
					// 删除节点时同时删除相关的线
					newVal?.forEach((task) => {
						task.edges = task.edges.filter(
							(edge) => edge.target !== inSideFlowNodeId
						);
					});
					setTasks && setTasks(newVal);
				}
				if (selectedEdge) {
					const newVal = tasksDeleteLine(selectedEdge);
					setTasks && setTasks(newVal);
				}
			}
		},
		[
			inSideFlowNodeId,
			ListenState,
			tasks,
			selectedEdge,
			setTasks,
			tasksDeleteLine,
		]
	);

	/** 监听鼠标按下事件 */
	useEffect(() => {
		window.addEventListener("keyup", deleteSelectFlowNode);
		return () => {
			window.removeEventListener("keyup", deleteSelectFlowNode);
		};
	}, [deleteSelectFlowNode]);

	/** 生成初始化node节点 */
	const initialNodes = useMemo(() => {
		const val: any = [];
		tasks?.length &&
			tasks.forEach((item) => {
				val.push({
					id: item.id,
					type: "flowNode",
					/** 每一项的数据 */
					data: {
						info: item,
						...{
							selectedStatus: inSideFlowNodeId === item.id,
							flowNodeStyle: {
								backgroundColor: "#fff",
								borderRadius: "4px",
							},
							inStyle: {
								backgroundColor: "rgba(19, 112, 255, 1)",
								border: "none",
								left: 12,
								top: "-2px",
							},
							outStyle: {
								backgroundColor: "rgba(19, 112, 255, 1)",
								border: "none",
								left: 12,
								bottom: "-2px",
							},
						},

						/** 样式 */
						style: {
							padding: "20px",
						},
						showVersion,
					},
					/** 坐标 */
					position: {
						x: Number(item.position?.x) || 0,
						y: Number(item.position?.y) || 0,
					},
					/**
					 * extent: "parent" 跟随父节点移动
					 * draggable: false 节点不可移动
					 */
					draggable: flowNodeDraggable,
				});
			});
		return val;
	}, [tasks, inSideFlowNodeId, flowNodeDraggable, showVersion]);

	/** 生成初始化的连线节点 */
	const initialEdges = useMemo(() => {
		const val: ILine[] = [];
		tasks?.length &&
			tasks.forEach((item) => {
				item?.edges?.length &&
					item?.edges.forEach((every) => {
						const newLine = {
							...every,
						};
						val.push(newLine);
					}, []);
			});
		return val.map((item: ILine) => {
			return {
				...item,
				/** 点击线选中 */
				...(selectedEdge?.id === item.id
					? {
							style: { stroke: "#1370FF", strokeWidth: 2 },
							animated: true,
					  }
					: {}),
				labelStyle: { fill: "#8A9099" },
				labelBgStyle: { fill: "#F7F8FA " },
				label: item.label ? `(${item.label})` : "",
			};
		});
	}, [selectedEdge?.id, tasks]);

	/** flowNode点击事件 */
	const onNodeClick = (e: any, node: Node) => {
		tasks?.forEach((item) => {
			if (item.id === node.id) {
				setInSideFlowNodeId(node.id);
			}
		});
		if (onFlowNodeClick) {
			onFlowNodeClick(node.id);
		}
		/** 点击node统一清除选中的edge */
		setSelectedEdge(undefined);
	};

	// 点击面板、画布
	const handlePaneClick = () => {
		setInSideFlowNodeId("");
		setSelectedEdge(undefined);
	};

	/** node节点 */
	const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
	/** 连线数组 */
	const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

	useEffect(() => {
		setEdges(initialEdges);
	}, [initialEdges, setEdges]);

	useEffect(() => {
		setNodes(initialNodes);
	}, [initialNodes, setNodes]);

	/** 节点拖动停止 */
	const onNodeDragStop = useCallback(
		(event: React.MouseEvent, node: Node) => {
			const newVal =
				(tasks?.length &&
					tasks.map((item) => {
						if (item.id === node.id) {
							return {
								...item,
								position: node.position,
							};
						} else {
							return item;
						}
					})) ||
				[];
			setTasks && setTasks(newVal);
		},
		[setTasks, tasks]
	);

	const connectModifyParameters = useCallback(
		(parameters: IParameter[], edgeItem: Connection) => {
			return parameters.map((item) => {
				if (item.name === edgeItem.targetHandle) {
					const { error, helperText } = getCustomTemplateParameterCheckResult({
						...item,
						linked: true,
						hidden: true,
					});

					return { ...item, linked: true, hidden: true, helperText, error };
				} else {
					return item;
				}
			});
		},
		[]
	);

	/** 获取连接线的端点类型 */
	const getClassType = useCallback(
		(connection: Connection) => {
			let inputClassType = "",
				outClassType: string | undefined = undefined;
			tasks?.length &&
				tasks.forEach((item) => {
					if ([connection.source, connection.target].includes(item.id)) {
						item.parameters.forEach((every) => {
							if (every.name === connection.targetHandle) {
								inputClassType = every.classType;
							}
							if (every.name === connection.sourceHandle) {
								outClassType = every.classType;
							}
						});
					}
				});
			return { inputClassType, outClassType };
		},
		[tasks]
	);

	/** 连接校验并修改值 */
	const connectCheck = useCallback(
		(connection: Connection) => {
			const newVal =
				(tasks?.length &&
					tasks?.map((item) => {
						if (item.id === connection.source) {
							return {
								...item,
								edges: [
									...item.edges,
									{
										...connection,
										id: uuid(),
									},
								],
							};
						} else if (item.id === connection.target) {
							return {
								...item,
								parameters: connectModifyParameters(
									item.parameters,
									connection
								),
							};
						} else {
							return item;
						}
					})) ||
				[];
			return newVal;
		},
		[connectModifyParameters, tasks]
	);

	/** 已经连接线啦 */
	const onConnect = useCallback(
		(connection: Connection) => {
			const { inputClassType, outClassType } = getClassType(connection);
			let result: ITask[] = [];
			if (inputClassType === outClassType) {
				result = connectCheck(connection) as ITask[];
			} else {
				Message.error("端口数据类型不一致，无法连接！");
				result = tasksDeleteLine(connection);
			}
			setTasks && setTasks(result);
		},
		[Message, connectCheck, getClassType, setTasks, tasksDeleteLine]
	);

	/** 点击连线 */
	const onEdgeClick = useCallback((e: any, val: Edge) => {
		setSelectedEdge(val);
		/** 点击连线清除选中的node ID */
		setInSideFlowNodeId("");
	}, []);

	const reactFlowParams =
		flowType === "edit"
			? {
					onNodesChange,
					onEdgesChange,
					onNodeDragStop,
					onConnect,
					onEdgeClick,
			  }
			: {};

	return (
		<ReactFlow
			className={styles.reactFlowBox}
			nodes={nodes}
			edges={edges}
			fitView={flowType === "default" ? true : false}
			{...reactFlowParams}
			nodeTypes={nodeTypes}
			onPaneClick={handlePaneClick}
			onNodeClick={onNodeClick}
			{...other}
		>
			{showControls && <Controls />}
			<Background color="#aaa" gap={16} />
		</ReactFlow>
	);
};

export default BatchOperatorFlow;
