<template>
	<a-drawer width="100%" :visible="show" @close="close" :destroy-on-close="true" :closable="false" class="debug_design_container">
		<a-spin :spinning="fullLoading" :tip="loadingTip">
			<a-layout>
				<a-layout-header class="tool_bar">
					<a-row>
						<a-col :span="4">
							<a-button @click="close" icon="caret-left" class="a_back"></a-button>
							&nbsp;
							<span>{{ type == 1 ? '模型':'口径'}}调试</span>
						</a-col>
						<a-col :span="12">
							<a-form-model ref="debugForm" :model="debugInfo" :label-col="{span: 8}" :wrapper-col="{span: 16}" layout="inline">
								<a-form-model-item :label="(type == 1 ? '模型':'口径') + '名称'" prop="linkId">
									<a-input v-model="showName" :disabled="!!debugInfo.linkId"></a-input>
								</a-form-model-item>
								<a-form-model-item label="增量时间" prop="time">
									<a-range-picker :locale="locale" :allowClear="false" :placeholder="['请选择', '请选择']" format="YYYY-MM-DD" v-model="timeVal" separator="至" @change="timeChange">
										<a-icon slot="suffixIcon" type="calendar" />
									</a-range-picker>
								</a-form-model-item>
							</a-form-model>
						</a-col>
						<a-col :span="8" align="right">
							<a-button type="primary" icon="cloud-server" @click="getFLinkRequestJson">
								计算引擎JSON
							</a-button>
							&nbsp;
							<a-button type="primary" icon="cloud-server" :loading="startLoading" :disabled="!startLoading && job.taskState=='running'" @click="debugStart()">
								抽取数据
							</a-button>
							&nbsp;
							<a-button icon="edit" type="primary" v-if="showEdit" @click="editCaliber">编辑{{ type == 1 ? '模型':'口径'}}</a-button>
							&nbsp;
							<a-button icon="fullscreen-exit" @click="close">关闭</a-button>
							&nbsp;
						</a-col>
					</a-row>
				</a-layout-header>
				<a-layout-content class="design_content">
					<drag-zone class="zone" ref="drag" :options="{ direction: 'vertical' }">
						<drag-content class="content setLink" ref="setLink">
							<a-card size="small" :bordered="false" title="数据链路">
								<div class="designer_container" ref="designer"></div>
							</a-card>
						</drag-content>
						<drag-handle class="handle">
							<a-icon type="caret-down" />
						</drag-handle>
						<drag-content class="content testBody" ref="testBody">
							<a-tabs size="small">
								<a-tab-pane key="1">
									<span slot="tab">
										日志信息
										<a-icon v-if="job.taskState=='running'" type="sync" spin />
									</span>
									<div class="log_info">
										<logCollapse :data="logData"></logCollapse>
									</div>
								</a-tab-pane>
								<a-tab-pane key="2" tab="数据调试">
									<div class="data_debug">
										<a-row>
											<a-col :span="11">
												<gl-sql-editor :read-only="true" :start-paras="true" v-model="sqlData" />
											</a-col>
											<a-col :span="2" align="center">
												<a-button type="primary" size="small" @click="getResult">查看结果</a-button>
											</a-col>
											<a-col :span="11">
												<vxe-grid ref="xGrid" :loading="resultLoading" show-overflow show-header-overflow v-bind="tableList" :border="true" :stripe="true" :resizable="true" height="100%"
													:scroll-y="{ gt: 10 }">
													<template #empty>
														<a-empty :image-style="{height: '80px',}">
															<span slot="description"> 暂无数据 </span>
														</a-empty>
													</template>
												</vxe-grid>
											</a-col>
										</a-row>
									</div>
								</a-tab-pane>
							</a-tabs>
						</drag-content>
					</drag-zone>
				</a-layout-content>
			</a-layout>
		</a-spin>
	</a-drawer>
</template>

<script>
	const { Graph, Dom } = require("@antv/x6");
	import '@antv/x6-vue-shape'
	const nodeComponent = require("./components/node.vue").default;
	const glSqlEditor = require("@/components/glSqlEditor").default;
	const $standardModel = require("@/api/datadevelopment/standardModel");
	const $indicatorApi = require("@/api/datadevelopment/indicatorApi");
	const logCollapse = require("@/components/logCollapse").default;

	export default {
		name: "debugDesign",
		components: {
			glSqlEditor,
			logCollapse
		},
		props: {
			value: {
				type: Boolean,
				default: false,
			},
			showEdit: {
				type: Boolean,
				default: true,
			},
			info: {
				type: Object,
				default: {}
			},
			type: {
				type: [Number, String],
				default: 1
			}
		},
		data() {
			return {
				debugType: 1,
				fullLoading: false,
				loadingTip: '',
				show: this.value,
				showName: '',
				dataInfo: this.info,
				debugInfo: {
					linkId: undefined,
					type: undefined,
					startDate: moment().format("YYYY-MM-DD"),
					endDate: moment().format("YYYY-MM-DD"),
					execNodeList: []
				},
				timeVal: [moment(), moment()],
				timer: null,
				startLoading: false,
				resultLoading: false,
				job: {},
				logData: [],
				sqlData: "",
				pager: {
					pageNum: 1,
					pageSize: 1000000
				},
				tableList: {}
			};
		},
		watch: {
			value(val) {
				this.show = val;
				if (val) {
					this.dataInfo = this.info;
					this.debugInfo.linkId = this.dataInfo.id;
					this.debugInfo.type = this.type;
					if (this.type == 1) {
						this.getModelData();
					}
					if (this.type == 2) {
						this.getIndicatorCaliberData();
					}
					this.getLinkData();
					this.getSql();
				}
			},
			show(val) {
				this.$emit("input", val);
			},
		},
		created() {},
		methods: {
			/**
			 * @Author: liujia
			 * @description: 获取计算引擎的json
			 */
			getFLinkRequestJson() {
				this.fullLoading = true;
				this.loadingTip = '正在下载计算引擎配置JSON文件';
				if (this.type == 1) {
					$standardModel.getCalcEngineReqJson({ id: this.info.id }).then(response => {
						this.downloadJson('model', response.data);
					}).finally(res => {
						this.fullLoading = false;
					});
				} else if (this.type == 2) {
					$indicatorApi.getCalcEngineReqJson({ id: this.info.id }).then(response => {
						this.downloadJson('indicatorCaliber', response.data);
					}).finally(res => {
						this.fullLoading = false;
					});
				}
			},
			/**
			 * @Author: liujia
			 * @description: 下载
			 */
			downloadJson(name, data) {
				window.URL = window.URL || window.webkitURL;
				let url = window.URL.createObjectURL(new Blob([JSON.stringify(data)]));
				let link = document.createElement("a");
				link.style.display = "none";
				link.href = url;
				link.setAttribute("download", `${name}-${this.info.id}-flink.json`);
				document.body.appendChild(link);
				link.click();
				window.URL.revokeObjectURL(url);
				this.fullLoading = false;
			},
			/**
			 * @Author: liujia
			 * @description: 获取模型数据
			 */
			getModelData() {
				$standardModel.getStandardModel({ id: this.info.id }).then(response => {
					if (response.data) {
						this.showName = response.data.name;
					}
				});
			},
			/**
			 * @Author: liujia
			 * @description: 获取模型数据
			 */
			getIndicatorCaliberData() {
				$indicatorApi.getIndicatorCaliberDetail({ id: this.info.id }).then(response => {
					if (response.data) {
						this.showName = response.data.datadevIndicatorCaliber.caliberName;
					}
				});
			},
			setJobStatus(execNodeList = [], taskState) {
				if (this.debugType == 1) {
					this.job.data.forEach(node => {
						node.status = null;
					})
					this.job.taskState = taskState;
				} else {
					if (execNodeList.length == 0) {
						this.job.taskState = taskState;
					} else {
						this.job.data.forEach(node => {
							if (execNodeList.find(nodeTemp => (nodeTemp.id == node.id || nodeTemp.id == node.nextId))) {
								node.status = taskState;
							} else {
								node.status = "none";
							}
						})
					}
				}
			},
			/**
			 * @Author: y_zp
			 * @description: 抽取数据
			 */
			debugStart(execNodeList = []) {
				this.debugType = execNodeList.length > 0 ? 2 : 1;
				if (!this.debugInfo.linkId) {
					this.$message.warn("请选择模型");
					return false;
				}

				this.debugInfo.execNodeList = execNodeList;
				this.setJobStatus(execNodeList, "running");
				this.startLoading = true;

				this.setCanvas(this.job);
				$standardModel
					.debugStart(this.debugInfo)
					.then((req) => {
						let id = req.data.taskId;
						this.getLog(id);
						this.timer = setInterval(() => {
							this.getLog(id)
						}, 1000 * 10);
					})
					.finally(() => {
						this.startLoading = false;
					})
			},
			/**
			 * @Author: y_zp
			 * @description: 获取监控状态以及日志内容
			 */
			getLog(id) {
				$standardModel
					.debugGetJobResult(id)
					.then((req) => {
						let info = req.data;
						try {
							this.logData = info.log;
							let status = "";
							if (["ABNORMAL", "FAILED"].indexOf(info.taskState) > -1) {
								status = "FAILED";
								this.setJobStatus(this.debugInfo.execNodeList, "FAILED");
							} else {
								status = info.taskState || "running";
							}
							this.setJobStatus(this.debugInfo.execNodeList, status);
							this.setCanvas(this.job);
							this.job.taskState = status.toLocaleLowerCase();
							if (["SUCCESS", "ABNORMAL", "FAILED", "PAUSED", "CANCELED"].indexOf(info.taskState) > -1) {
								clearInterval(this.timer)
							}
						} catch (e) {
							this.logData = [];
						}
					})
			},
			/**
			 * @Author: y_zp
			 * @description: 获取日志sql
			 */
			getSql() {
				$standardModel
					.debugGetSql(this.debugInfo)
					.then((req) => {
						this.sqlData = req.data;
					})
			},
			/**
			 * @Author: liujia
			 * @description: 查看结果
			 */
			getResult() {
				if (this.type == 1) {
					this.getModelResult();
				} else if (this.type == 2) {
					this.getIndicatorResult();
				}
			},
			/**
			 * @Author: liujia
			 * @description: 查看指标结果
			 */
			getIndicatorResult() {
				this.resultLoading = true;
				$indicatorApi
					.previewIndicatorDetail({
						model: { id: this.dataInfo.id },
						queryParams: {
							extColumns: '', //显示的字段名 ‘，’分割
						},
						...this.pager
					})
					.then((req) => {
						let info = req.data;
						let columns = [{ type: 'seq', title: "序号", width: 50 }];
						if (info.length > 0) {
							Object.keys(info[0]).forEach(item => {
								let width = item.length * 5;
								if (width < 100) {
									width = 100;
								}
								columns.push({
									field: item,
									title: item,
									width: `${width}px`
								})
							})
							this.tableList.columns = columns;
							this.tableList.data = info;
						}
					})
					.finally(() => {
						this.resultLoading = false;
					})
			},
			/**
			 * @Author: y_zp
			 * @description: 查看模型结果
			 */
			getModelResult() {
				this.resultLoading = true;
				$standardModel
					.previewModelDetail({
						model: { id: this.dataInfo.id },
						queryParams: {
							extColumns: '', //显示的字段名 ‘，’分割
						},
						...this.pager
					})
					.then((req) => {
						let info = req.data;
						let columns = [{ type: 'seq', title: "序号", width: 50 }];
						if (info.length > 0) {
							Object.keys(info[0]).forEach(item => {
								let width = item.length * 5;
								if (width < 100) {
									width = 100;
								}
								columns.push({
									field: item,
									title: item,
									width: `${width}px`
								})
							})
							this.tableList.columns = columns;
							this.tableList.data = info;
						}
					})
					.finally(() => {
						this.resultLoading = false;
					})
			},
			/**
			 * @Author: y_zp
			 * @description: 增量时间切换
			 */
			timeChange(date, dateString) {
				this.debugInfo.startDate = dateString[0];
				this.debugInfo.endDate = dateString[1];
			},
			/**
			 * @Author: y_zp
			 * @description: 获取数据链路
			 */
			getLinkData() {
				this.startLoading = true;
				$standardModel
					.debugGetNodeList(this.debugInfo)
					.then((req) => {
						let info = req.data;
						if (info.length > 0) {
							this.job = {
								id: this.debugInfo.linkId,
								graph: undefined, //画布
								data: this.getNodePosition(info),
								taskState: ""
							};
							this.registerNode();
							this.initCanvas();
						}
					})
					.finally(() => {
						this.startLoading = false;
					})
			},
			/**
			 * @Author: y_zp
			 * @description: 注册节点
			 */
			registerNode() {
				const node = {
					name: 'custom-lane',
					options: {
						inherit: 'vue-shape',
						width: 200,
					}
				}
				Graph.registerNode(node.name, node.options, true)
			},
			/**
			 * @Author: y_zp
			 * @description: 链路数据绘制
			 */
			initCanvas() {
				this.$nextTick(() => {
					const graphOpetion = this.getGraphOpetion();
					this.job.graph = new Graph(graphOpetion);
					if (this.job.data) {
						try {
							this.setCanvas(this.job);
						} catch (e) {
							console.error(e);
						}
					}
				})
			},
			/**
			 * @Author: y_zp
			 * @description: 设置节点位置信息
			 */
			getNodePosition(data) {
				let _width = this.$refs.designer.scrollWidth
				const result = [];
				const itemMap = {};
				// 数据转化为一棵树
				data.forEach(item => {
					const id = item.id;
					const nextId = item.nextId;
					if (!itemMap[id]) {
						itemMap[id] = {
							children: [],
						};
					}
					const treeItem = {
						...item,
						children: itemMap[id]["children"],
					};

					if (nextId == null) {
						result.push(treeItem);
					} else {
						if (!itemMap[nextId]) {
							itemMap[nextId] = {
								children: [],
							};
						}
						itemMap[nextId].children.push(treeItem);
					}
				})
				// 设置位置信息
				const setPosition = (data, i) => {
					let num = i + 1;
					data.forEach((item, index) => {
						item.position = {
							x: _width - (400 + num * 320),
							y: 100 + index * 60
						}
						item.debugFlag = index == 0
						if (item.children.length > 0) {
							setPosition(item.children, num);
						}
					})
				}

				setPosition(result, 0);

				const flatten = (arr) => {
					return arr.reduce((prev, cur) => {
						const { children = [], ...others } = cur;
						return prev.concat([{ ...others }], flatten(children));
					}, []);
				}

				return flatten(result)
			},

			/**
			 * @Author: y_zp
			 * @description: 绘制链路图
			 */
			setCanvas(job) {
				const cells = [];
				job.data.forEach(node => {
					let status = job.taskState.toLocaleLowerCase() || "";
					if (node.status) {
						status = node.status.toLocaleLowerCase();
					}
					let cell = {
						label: node.name,
						width: 200,
						height: 40,
						id: node.id,
						shape: "custom-lane",
						position: node.position,
						component: this.renderNodeBtn(node, status)
					}
					cells.push(job.graph.createNode(cell));

					if (node.nextId) {
						let nodeItem = {
							shape: "edge",
							id: `${node.id}${node.nextId}`,
							source: node.id,
							// target: node.nextId,
							label: {
								data: node,
								position: 50
							},
							attrs: {
								line: {
									stroke: "#c9c9c9",
									strokeWidth: 1,
									strokeDasharray: 3,
								}
							}
						}

						if (node.debugFlag) {
							nodeItem.target = node.nextId;
						} else {
							let nextNodeItem = job.data.find(item => { return item.id == node.nextId })
							nodeItem.target = {
								x: nextNodeItem.position.x - 100,
								y: nextNodeItem.position.y + 20
							}
							nodeItem.attrs.line.targetMarker = null;
						}

						cells.push(job.graph.createEdge(nodeItem))
					}
				});
				job.graph.resetCells(cells);
				job.graph.centerContent();
			},

			renderNodeBtn(node, status) {
				return {
					template: `<node-component />`,
					provide() {
						return {
							nodeData: { ...node, status }
						}
					},
					components: {
						nodeComponent
					},
				}
			},

			getGraphOpetion() {
				let _this = this;
				return {
					container: this.$refs.designer, //容器对象 //容器对象
					autoResize: true, //是否允许更新容器大小
					height: "100%",
					width: "100%",
					panning: {
						//支持水平平移
						enabled: true,
						eventTypes: ["leftMouseDown", "rightMouseDown", "mouseWheel"],
					},
					mousewheel: {
						//允许缩放
						enabled: true, //启动
						zoomAtMousePosition: true,
						modifiers: "ctrl",
						minScale: 0.5,
						maxScale: 3,
					},
					connecting: {
						//连线规则
						router: {
							name: "manhattan",
							args: { padding: 1 },
						},
					},
					interacting: {
						nodeMovable: false,
						edgeMovable: false
					},
					onEdgeLabelRendered: (args) => {
						const { label, container, selectors } = args
						if (label.data.debugFlag) {
							const content = this.appendForeignObject(container)
							const btn = document.createElement('button')
							btn.appendChild(document.createTextNode('调试'))
							btn.style.height = "24px"
							btn.style.lineHeight = "1"
							btn.style.fontSize = "12px"
							btn.style.border = "none"
							btn.style.borderRadius = "4px"
							btn.style.background = "linear-gradient(133deg, #69CAFF 0%, #2B6FF0 100%)"
							btn.style.color = "#fff"
							btn.addEventListener('click', () => {
								// alert('clicked')
								let execNodeList = [];
								let nextNode = this.job.data.find(item => item.id == label.data.nextId);
								if (nextNode) {
									let node = {
										id: nextNode.id,
										type: nextNode.nodeType,
									}
									execNodeList.push(node);
									this.debugStart(execNodeList);
								}
								// _this.$message.warn("功能开发中")
							})
							content.appendChild(btn)
						}

					}
				};
			},

			appendForeignObject(container) {
				const fo = Dom.createSvgElement('foreignObject')
				const body = Dom.createElementNS('body', Dom.ns.xhtml)
				const content = Dom.createElementNS('div', Dom.ns.xhtml)

				fo.setAttribute('width', '110')
				fo.setAttribute('height', '28')
				fo.setAttribute('x', '-50')
				fo.setAttribute('y', '-15')

				body.setAttribute('xmlns', Dom.ns.xhtml)
				body.style.width = '100%'
				body.style.height = '100%'
				body.style.padding = '0'
				body.style.margin = '0'
				body.style.background = 'none'
				body.style.minWidth = 'auto'

				content.style.width = '100%'
				content.style.height = '100%'
				content.style.textAlign = 'center'
				content.style.lineHeight = '28px'
				content.style.minWidth = 'auto'

				body.appendChild(content)
				fo.appendChild(body)
				container.appendChild(fo)

				return content
			},

			close() {
				this.show = false
				this.logData = [];
				this.tableList = {}
				clearInterval(this.timer)
				this.job.graph && this.job.graph.dispose(); //销毁画布
			},
			editCaliber() {
				this.show = false
				this.$emit('edit-caliber')
			}
		},
		beforeDestroy() {
			clearInterval(this.timer)
		}
	};
</script>
<style lang="less">
	@import "/src/themes/config.less";

	.debug_design_container {

		.x6-node {
			foreignObject {
				body {
					min-width: auto !important;
				}
			}
		}

		.ant-drawer-body {
			padding: 0;
			height: 100%;

			.ant-layout {
				height: 100%;

				.tool_bar {
					background: #fff;
					box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.1);
					border-bottom: 1px solid @borderColor;
					position: relative;
					padding: 0;

					.ant-btn {
						border-radius: 20px;
					}

					.a_back {
						width: 48px;
						height: 48px;
						border: 0;
						font-size: 20px;
						color: rgba(0, 0, 0, 0.7);
						border-radius: 0;
						border-right: 1px solid @borderColor;
						border-bottom: 1px solid @borderColor;

						&:hover {
							background: rgba(0, 0, 0, 0.1);
						}
					}

					.ant-select {
						width: 160px;
					}

					.ant-calendar-picker-input {
						width: 250px;
					}
				}

				.design_content {
					background: #fff;
					padding: 6px;
					height: 100%;

					.drag-zone {
						height: 100%;

						.drag-content {
							overflow: auto;
						}

						.setLink {
							height: calc(100vh - 440px);
						}

						.testBody {
							height: 365px;
						}

						.handle {
							height: 8px;
							text-align: center;
							font-size: 20px;
							line-height: 0px;
							background: #eff2fd;
							position: relative;

							i {
								position: absolute;
								display: flex;
								justify-content: center;
								align-items: center;
								height: 8px;
								width: 90px;
								left: 50%;
								margin-left: -45px;
								border: 1px solid @borderColor;
								border-bottom: 0;
								border-top-left-radius: 8px;
								border-top-right-radius: 8px;
								background: #fafafa;
								color: #000000;
								cursor: pointer;

								svg {
									width: 8px;
								}

								&:hover {
									background: #ffff;
								}
							}
						}

						.ant-tabs {
							height: 100%;

							.ant-tabs-content {
								margin-top: -15px;
								height: calc(100% - 40px);
							}
						}

						.log_info,
						.data_debug {
							padding: 12px 12px 0;
							height: 100%;
						}

						.log_info {
							overflow: hidden;
							overflow-y: scroll;
						}

						.data_debug {

							.ant-row,
							.ant-col {
								height: 100%;
							}

							.gl_sql_editor {
								border: 1px solid #e8e8e8;
							}

							.ant-btn {
								position: absolute;
								top: 50%;
								transform: translateX(-50%)
							}
						}

						.designer_container {
							width: 100%;
							height: 100%;
							height: calc(100vh - 488px);
							position: relative;
						}
					}
				}
			}

		}
	}
</style>