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 { IEdge, 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";

/*
 * @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 */
	setSelectedBatchNodeId?: (val: string) => void;
	/** 选中的batch节点id */
	selectedBatchNodeId?: 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,
		setSelectedBatchNodeId,
		selectedBatchNodeId,
		type: flowType = "default",
		setTasks,
		onFlowNodeClick,
		ListenState = true,
		showControls = true,
		...other
	} = props;
	/** 自定义的节点类型 */
	const nodeTypes = useMemo(() => {
		return { batchNode: BatchNode, flowNode: FlowNode };
	}, []);
	console.log(tasks, "11111");
	/** 内部维护的选择的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) => {
			const 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.sourceHandle !== connection.sourceHandle
									)) ||
								[];

							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;
						}
					})) ||
				[];
			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
								);
							})) ||
						[];
					setTasks && setTasks(newVal);
				}
				if (selectedEdge) {
					const newVal = tasksDeleteLine(selectedEdge);
					setTasks && setTasks(newVal);
				}
			}
		},
		[inSideBatchNodeId, selectedEdge, setTasks, tasks, tasksDeleteLine]
	);

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

	/** 获取是否有输入节点或者是否有输出节点 */
	const nodesInputAndOutputStatus = useCallback(
		(id: string) => {
			/** 所有的连线 */
			const lineArr: IEdge[] = [];
			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 = 22;
			if (positionXArr?.length) {
				const val = positionXArr[positionXArr.length - 1] + 150;
				width = val > 176 ? val : width;
			}
			if (positionYArr?.length) {
				const val = positionYArr[positionYArr.length - 1] + 6;
				height = val > 22 ? 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,
						...(item.type === "BATCH"
							? {
									/** flow组件类型 */
									flowType,
									/** 是否有流节点 */
									isFlowNode: isFlowNode(item.id),
									/** 选中状态 */
									selectedStatus: selectedBatchNodeId
										? selectedBatchNodeId === item.id
										: inSideBatchNodeId === item.id,
									/** tasks 数据 */
									tasks: tasks,
							  }
							: { selectedStatus: inSideFlowNodeId === item.id }),
						/** 输入输出圆点状态 */
						dotStatus: nodesInputAndOutputStatus(item.id),

						/** 样式 */
						style: {
							...getBatchStyle(item),
							padding: isFlowNode(item.id) ? "20px" : "12px 20px",
						},
					},
					/** 坐标 */
					position: {
						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,
		selectedBatchNodeId,
		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 = selectedBatchNodeId
				? selectedBatchNodeId
				: inSideBatchNodeId;
			return {
				...item,
				// type: "smoothstep",
				/** 点击线选中 */
				...(selectedEdge?.id === item.id
					? {
							style: { stroke: "#1370FF", strokeWidth: 2 },
							animated: true,
					  }
					: {}),
				/** 点击batch节点选中 */
				...(item?.batchId === newSelectId
					? { style: { stroke: "#1370FF" }, animated: true }
					: {}),
				labelStyle: { fill: "#8A9099" },
				labelBgStyle: { fill: "#F7F8FA " },
				label: item.label ? `(${item.label})` : "",
			};
		});
	}, [inSideBatchNodeId, selectedBatchNodeId, selectedEdge?.id, tasks]);

	/** 设置nodeId方法 */
	const setNodeIdFun = useCallback(
		(id: string) => {
			setSelectedBatchNodeId
				? setSelectedBatchNodeId(id)
				: setInSideBatchNodeId(id);
			onBatchClick && onBatchClick(id);
			setInSideFlowNodeId("");
			document.getElementById(`point${id}`)?.scrollIntoView(true);
		},
		[onBatchClick, setSelectedBatchNodeId]
	);

	/** 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("");

					setSelectedBatchNodeId && setSelectedBatchNodeId("");
				}
			}
		});
		if (onFlowNodeClick) {
			onFlowNodeClick(node.id);
		}
		/** 点击node统一清除选中的edge */
		setSelectedEdge(undefined);
	};

	const handlePaneClick = () => {
		setSelectedBatchNodeId
			? setSelectedBatchNodeId("")
			: setInSideBatchNodeId("");
		setInSideFlowNodeId("");
		onBatchClick && onBatchClick("");
		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("");
			setInSideBatchNodeId("");
			setSelectedBatchNodeId && setSelectedBatchNodeId("");
		},
		[setSelectedBatchNodeId]
	);

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

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

export default Flow;
