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 "./components/BatchNode";
import FlowNode from "./components/FlowNode";
import { getCustomTemplateParameterCheckResult } from "@/views/WorkFlowEdit/util";
import { useMessage } from "@/components/MySnackbar";

import styles from "./index.module.css";

/*
 * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
 * @Date: 2022-06-22 10:15:22
 * @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
 * @LastEditTime: 2022-06-22 11:10:35
 * @FilePath: /bkunyun/src/views/Project/components/Flow/index.tsx
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
interface IProps extends ReactFlowProps {
	tasks?: ITask[];
	/** 点击batch事件 */
	onBatchClick?: (val: string) => void;
	/** 设置选中的batch节点id */
	setExternalSelectedNodeId?: (val: string) => void;
	/** 选中的batch节点id */
	externalSelectedNodeId?: string;
	/** 类型， edit为编辑类型 */
	type?: "edit" | "default";
	/** 设置组件数据 组件为编辑状态使用 */
	setTasks?: (val: ITask[]) => void;
	/** 点击流程node 节点 返回唯一标识符 */
	onFlowNodeClick?: (val: string) => void;
	/** 监听事件的状态 */
	ListenState?: boolean;
	// 是否显示Controls（放大缩小全屏等按钮）
	showControls?: boolean;
}

const Flow = (props: IProps) => {
	const {
		tasks,
		onBatchClick,
		setExternalSelectedNodeId,
		externalSelectedNodeId,
		type: flowType = "default",
		setTasks,
		onFlowNodeClick,
		ListenState = true,
		showControls = true,
		...other
	} = props;

	/** 自定义的节点类型 */
	const nodeTypes = useMemo(() => {
		return { batchNode: BatchNode, flowNode: FlowNode };
	}, []);
	/** 内部维护的选择的batch节点Id */
	const [inSideBatchNodeId, setInSideBatchNodeId] = useState<string>("");
	/** 内部维护的选择的flow节点Id */
	const [inSideFlowNodeId, setInSideFlowNodeId] = useState<string>("");
	/** 选中的线 */
	const [selectedEdge, setSelectedEdge] = useState<Edge>();
	const Message = useMessage();

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

							return {
								...item,
								edges: newEdges,
							};
							/** 选中batch结束位置&&更新校验值 */
						} else if (item.id === connection.target && item.type === "BATCH") {
							const newParameters =
								(item.parameters?.length &&
									item.parameters.map((every) => {
										if (every.name === connection.targetHandle) {
											const { error, helperText } =
												getCustomTemplateParameterCheckResult({
													...every,
													linked: false,
													hidden: false,
												});
											return {
												...every,
												hidden: false,
												error,
												helperText,
											};
										} else {
											return every;
										}
									})) ||
								[];
							return {
								...item,
								parameters: newParameters,
							};
						} else {
							return item;
						}
					})) ||
				[];
			// 更新批节点下对应流节点的hidden和错误提示
			result = result.map((task) => {
				if (task.parentNode === connection.target) {
					let isCheck = true;
					const parametersChange = task.parameters.map((parameter) => {
						if (parameter.name === connection.targetHandle) {
							const { error, helperText } =
								getCustomTemplateParameterCheckResult({
									...parameter,
									linked: false,
									hidden: false,
								});
							return {
								...parameter,
								hidden: false,
								linked: false,
								error,
								helperText,
							};
						} else {
							return parameter;
						}
					});
					parametersChange.forEach((parameter) => {
						if (
							getCustomTemplateParameterCheckResult(parameter).error === true
						) {
							isCheck = false;
						}
					});
					return {
						...task,
						isCheck,
						parameters: parametersChange,
					};
				} else {
					return task;
				}
			});
			return result;
		},
		[tasks]
	);

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

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

	/** 获取是否有输入节点或者是否有输出节点 */
	// const nodesInputAndOutputStatus = useCallback(
	// 	(id: string) => {
	// 		/** 所有的连线 */
	// 		const lineArr: ] = [];
	// 		tasks?.length &&
	// 			tasks.forEach((item) => {
	// 				item.edges?.length && lineArr.push(...item.edges);
	// 			});
	// 		/** 所有的输入节点ID */
	// 		const isInput = lineArr?.some((item) => item.target === id);
	// 		/** 所有的输出节点ID */
	// 		const isOutput = lineArr?.some((item) => item.source === id);
	// 		return {
	// 			isInput,
	// 			isOutput,
	// 		};
	// 	},
	// 	[tasks]
	// );

	/** 获取是否有流节点 */
	const isFlowNode = useCallback(
		(id: string) => {
			return (
				tasks?.length &&
				tasks?.some((item) => {
					return item.parentNode === id;
				})
			);
		},
		[tasks]
	);

	/** 通过子flow节点计算batch节点的样式 */
	const getBatchStyle = useCallback(
		(value: ITask) => {
			const positionXArr: number[] = [];
			const positionYArr: number[] = [];
			tasks?.length &&
				tasks?.forEach((item) => {
					if (item.parentNode === value.id) {
						positionXArr.push(item.position?.x || 0);
						positionYArr.push(item.position?.y || 0);
					}
				});
			positionXArr.sort((a, b) => {
				return a - b;
			});
			positionYArr.sort((a, b) => {
				return a - b;
			});
			let width = 176,
				height = 12;
			if (positionXArr?.length) {
				const val = positionXArr[positionXArr.length - 1] + 144;
				width = val > 176 ? val : width;
			}
			if (positionYArr?.length) {
				const val = positionYArr[positionYArr.length - 1] + 74;
				height = val > 12 ? val : height;
			}
			return {
				width,
				height,
			};
		},
		[tasks]
	);

	/** 生成初始化node节点 */
	const initialNodes = useMemo(() => {
		const val: any = [];
		tasks?.length &&
			tasks.forEach((item) => {
				val.push({
					id: item.id,
					type: item.type === "BATCH" ? "batchNode" : "flowNode",
					/** 每一项的数据 */
					data: {
						info: item,
						/** flow组件类型 */
						flowType,
						...(item.type === "BATCH"
							? {
									/** 是否有流节点 */
									isFlowNode: isFlowNode(item.id),
									/** 选中状态 */
									selectedStatus: externalSelectedNodeId
										? externalSelectedNodeId.includes(item.id)
										: inSideBatchNodeId === item.id,
									/** tasks 数据 */
									tasks: tasks,
							  }
							: {
									selectedStatus: externalSelectedNodeId
										? externalSelectedNodeId.includes(item.id)
										: inSideFlowNodeId === item.id,
							  }),
						/** 输入输出圆点状态 */
						// dotStatus: nodesInputAndOutputStatus(item.id),

						/** 样式 */
						style: {
							...getBatchStyle(item),
							marginTop: "-44px",
							padding: "12px 20px 20px 20px",
							visibility: 'visible',
						},
					},
					/** 坐标 */
					position: {
						/** 流算子生成的时候多加了15  兼容历史模版数据  直接这边减去15 */
						x: Number(item.position?.x) || 0,
						y: Number(item.position?.y) || 0,
					},
					/**
					 * extent: "parent" 跟随父节点移动
					 * draggable: false 节点不可移动
					 */
					...(item.type === "BATCH"
						? { style: { zIndex: -1 }, extent: "parent" }
						: { draggable: false }),
					/** parentNode 父节点ID */
					...(item.parentNode ? { parentNode: item.parentNode } : {}),
				});
			});
		return val;
	}, [
		tasks,
		flowType,
		isFlowNode,
		externalSelectedNodeId,
		inSideBatchNodeId,
		inSideFlowNodeId,
		// nodesInputAndOutputStatus,
		getBatchStyle,
	]);

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

	/** 设置nodeId方法 */
	const setNodeIdFun = useCallback(
		(id: string) => {
			setExternalSelectedNodeId
				? setExternalSelectedNodeId(id)
				: setInSideBatchNodeId(id);
			onBatchClick && onBatchClick(id);
			setInSideFlowNodeId("");
		},
		[onBatchClick, setExternalSelectedNodeId]
	);

	/** flowNode点击事件 */
	const onNodeClick = (e: any, node: Node) => {
		tasks?.forEach((item) => {
			if (item.id === node.id) {
				if (item.type === "BATCH") {
					setNodeIdFun(node.id);
				} else {
					setInSideFlowNodeId(node.id);
					setInSideBatchNodeId("");
					setExternalSelectedNodeId && setExternalSelectedNodeId(node.id);
				}
				document.getElementById(`point${node.id}`)?.scrollIntoView(true);
			}
		});
		if (onFlowNodeClick) {
			onFlowNodeClick(node.id);
		}
		/** 点击node统一清除选中的edge */
		setSelectedEdge(undefined);
	};

	const handlePaneClick = () => {
		setExternalSelectedNodeId
			? setExternalSelectedNodeId("")
			: setInSideBatchNodeId("");
		setInSideFlowNodeId("");
		onBatchClick && onBatchClick("");
		setSelectedEdge(undefined);
		onFlowNodeClick && onFlowNodeClick("");
	};

	/** 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) => {
			let 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;
						}
					})) ||
				[];
			//  更新批节点下对应流节点的hidden和错误提示
			newVal = newVal.map((task) => {
				if (task.parentNode === connection.target) {
					let isCheck = true; // 红点校验
					let parametersChange = task.parameters.map((parameter) => {
						if (parameter.name === connection.targetHandle) {
							const { error, helperText } =
								getCustomTemplateParameterCheckResult({
									...parameter,
									linked: true,
									hidden: true,
								});
							return {
								...parameter,
								hidden: true,
								linked: true,
								error,
								helperText,
							};
						} else {
							return parameter;
						}
					});
					parametersChange.forEach((parameter) => {
						if (
							getCustomTemplateParameterCheckResult(parameter).error === true
						) {
							isCheck = false;
						}
					});
					return {
						...task,
						isCheck,
						parameters: parametersChange,
					};
				} else {
					return task;
				}
			});

			return newVal;
		},
		[connectModifyParameters, tasks]
	);

	// 获取算子类型   批算还是流算子
	const getTaskType = useCallback(
		(taskId: string) => {
			let type = "";
			tasks?.forEach((task) => {
				if (task.id === taskId) {
					type = task.type;
				}
			});
			return type;
		},
		[tasks]
	);

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

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

	// const onNodesChange = (val: any)=>{
	// 	// 自定义change事件  不允许react flow组件本身删除事件
	// 	// return
	// }

	// const onEdgesChange = (val: any) =>{
	// 	// 自定义change事件  不允许react flow组件本身删除事件
	// 	// return;
	// }

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

	return (
		<ReactFlow
			className={styles.reactFlowBox}
			nodes={nodes}
			edges={edges}
			{...reactFlowParams}
			// proOptions={{ hideAttribution: true, account: "" }}
			nodeTypes={nodeTypes}
			onPaneClick={handlePaneClick}
			onNodeClick={onNodeClick}
			{...other}
		>
			{showControls && <Controls />}
			<Background color="#aaa" gap={16} />
		</ReactFlow>
	);
};

export default Flow;
