<template>
	<view class="w-process" v-if="!data.loading && data.processTasks.length > 0">
		<view class="w-process-line"></view>
		<view class="w-process-render" v-for="node in data.processTasks" :key="node.id">
			<process-node-render :ref="node.id" class="w-node-render" :task="node" @addOrg="addOrg" @delOrg="delOrg" />
		</view>
		<org-picker ref="orgPicker" :type="data.selectedNode.type || 'user'"
			:multiple="data.selectedNode.multiple || false" :selected="data.selectedNode.users" @ok="doAddOrg" />
	</view>
</template>

<script setup>
	import {
		nextTick,
		reactive,
		computed,
		onBeforeMount,
		getCurrentInstance,
		watch,
		ref
	} from 'vue';
	import {
		$deepCopy
	} from '@/utils/tool.js'
	import ProcessNodeRender from "./ProcessNodeRender.vue";
	import {
		forEachNode
	} from '@/utils/ProcessUtil.js'
	import processApi from '@/api/process'
	import OrgPicker from "@/components/OrgPicker.vue";
	import {
		getGroupModels
	} from "@/api/model";
	import {
		debounce
	} from '@/utils/tool.js'

	const instance = getCurrentInstance()


	//小程序端不支持jsx，真是艹了，这块要重写😂
	const props = defineProps({
		processDefId: String,
		process: {
			type: Object,
			default: () => {
				return {}
			}
		},
		formData: {
			type: Object,
			default: () => {
				return {}
			}
		},
		modelValue: {
			type: Object,
			default: () => {
				return {}
			}
		},
		deptId: {
			type: String,
			default: null
		},
	})

	const _value = computed({
		get() {
			return props.modelValue
		},
		set(val) {
			emits('update:modelValue', val)
		}
	})

	defineExpose({
		validate
	})
	const emits = defineEmits(['update:modelValue', 'render-ok'])

	const loginUser = JSON.parse(uni.getStorageSync('loginUser'))
	loginUser.value = {
		id: loginUser.userId,
		name: loginUser.realName,
		avatar: loginUser.avatar,
		sn: loginUser.sn
	}

	const orgPicker = ref()
	const data = reactive({
		selectUserNodes: new Set(),
		loading: false,
		selectedNode: {},
		reverse: false,
		userCatch: {},
		oldFormData: {},
		models: null,
		processTasks: [],
		conditionFormItem: new Set(),
		branchNodeMap: new Map(),
		loadingReqs: [],
		calls: []
	})

	onBeforeMount(() => loadProcessRender())

	//流程渲染去抖动
	const processRenderDebounce = debounce(() => loadProcessRender(), 500)

	async function loadProcessRender() {
		console.log('渲染流程')
		data.loading = true
		data.processTasks.length = 0
		data.selectUserNodes.clear()
		data.loadingReqs.length = 0
		//TODO 从这里可以使用去抖动函数 this.$debounce
		loadProcess(props.process, data.processTasks)
		data.processTasks.push({
			title: '结束',
			name: 'END',
			icon: 'checkbox-filled',
			enableEdit: false
		})
		if (data.loadingReqs.length > 0) {
			Promise.all(data.loadingReqs).then(() => {
				data.loading = false
				emits('render-ok')
			}).catch(() => data.loading = false)
		} else {
			emits('render-ok')
			data.loading = false
		}
	}

	function loadProcess(processNode, processTasks, bnode, bid) {
		forEachNode(processNode, node => {
			if (bnode) { //如果是分支内子节点
				data.branchNodeMap.set(node.id, {
					node: bnode,
					id: bid
				})
			}
			switch (node.type) {
				case 'ROOT':
					processTasks.push({
						id: node.id,
						title: node.name,
						name: '发起人',
						desc: `${loginUser.value.name} 将发起本流程`,
						icon: 'contact-filled',
						enableEdit: false,
						users: [loginUser.value]
					});
					break;
				case 'APPROVAL':
					processTasks.push(getApprovalNode(node))
					break;
				case 'TASK':
					processTasks.push(getApprovalNode(node, false))
					break;
				case 'SUBPROC':
					processTasks.push(getSubProcNode(node))
					break;
				case 'CC':
					processTasks.push(getCcNode(node))
					break;
				case 'CONDITIONS': //条件节点选一项
					processTasks.push(getConditionNode(node, bnode, bid))
					loadProcess(node.children, processTasks)
					return true
				case 'INCLUSIVES': //包容分支会执行所有符合条件的分支
					processTasks.push(getInclusiveNode(node, bnode, bid))
					loadProcess(node.children, processTasks)
					return true
				case 'CONCURRENTS': //并行分支无条件执行所有分支
					processTasks.push(getConcurrentNode(node, bnode, bid))
					loadProcess(node.children, processTasks)
					return true
			}
		})
	}

	function getSubProcNode(node) {
		let user = {}
		//提取发起人
		switch (node.props.staterUser.type) {
			case "ROOT":
				user = loginUser.value;
				break;
			case "FORM":
				const fd = props.formData[node.props.staterUser.value]
				user = Array.isArray(fd) && fd.length > 0 ? fd[0] : {
					name: '请选人'
				};
				break;
			case "SELECT":
				user = node.props.staterUser.value || {};
				break;
		}
		const procNode = {
			id: node.id,
			title: `${node.name} [由${user.id ? user.name : '?'}发起]`,
			name: '子流程',
			desc: '',
			icon: 'more-filled',
			enableEdit: false,
			users: [user]
		}
		getSubModel(() => {
			procNode.desc = `调用子流程 [${data.models[node.props.subProcCode]}]`
		})
		return procNode
	}

	function getApprovalNode(node, isApproval = true) {
		let result = {
			id: node.id,
			title: node.name,
			name: isApproval ? '审批人' : '办理人',
			icon: isApproval ? 'person-filled' : 'calendar-filled',
			enableEdit: false,
			multiple: false,
			mode: node.props.mode,
			users: [],
			desc: ''
		}
		let loadCatch = true
		switch (node.props.assignedType) {
			case 'ASSIGN_USER':
				result.users = $deepCopy(node.props.assignedUser)
				result.desc = isApproval ? '指定审批人' : '指定办理人'
				break
			case 'ASSIGN_LEADER':
				data.loadingReqs.push(
					processApi.getLeaderByDepts((node.props.assignedDept || []).map(d => d.id)).then(res => {
						result.users = res.data
					}))
				result.desc = '指定部门的领导'
				break
			case 'SELF':
				result.users = [loginUser.value]
				result.desc = `发起人自己${isApproval?'审批':'办理'}`
				break
			case 'SELF_SELECT':
				result.enableEdit = true
				data.selectUserNodes.add(node.id)
				result.multiple = node.props.selfSelect.multiple || false
				result.desc = isApproval ? '自选审批人' : '自选办理人'
				break
			case 'LEADER_TOP':
				result.desc = `连续多级主管${isApproval?'审批':'办理'}`
				const leaderTop = node.props.leaderTop
				data.loadingReqs.push(
					processApi.getUserLeaders(
						'TOP' === leaderTop.endCondition ? 0 : leaderTop.endLevel,
						props.deptId, leaderTop.skipEmpty).then(res => {
						result.users = res.data
					}))
				break
			case 'LEADER':
				result.desc = node.props.leader.level === 1 ?
					`直接主管${isApproval?'审批':'办理'}` :
					`第${node.props.leader.level}级主管${isApproval?'审批':'办理'}`
				data.loadingReqs.push(
					processApi.getUserLeader(
						node.props.leader.level,
						props.deptId,
						node.props.leader.skipEmpty).then(res => {
						result.users = res.data ? [res.data] : []
					}))
				break
			case 'ROLE':
				result.desc = `由角色[${(node.props.role || []).map(r => r.name)}]${isApproval?'审批':'办理'}`
				data.loadingReqs.push(
					processApi.getUsersByRoles({
						projectSn: loginUser.value.sn,
						roleIds: (node.props.role || []).map(r => r.id)
					}).then(res => {
						result.users = res.data
					}))
				break
			case 'FORM_USER':
				loadCatch = false
				result.desc = `由表单字段内人员${isApproval?'审批':'办理'}`
				data.conditionFormItem.add(node.props.formUser)
				result.users = props.formData[node.props.formUser] || []
				break
			case 'FORM_DEPT':
				loadCatch = false
				result.desc = `由表单部门内主管${isApproval?'审批':'办理'}`
				data.conditionFormItem.add(node.props.formDept)
				data.loadingReqs.push(
					processApi.getLeaderByDepts((props.formData[node.props.formDept] || []).map(d => d.id)).then(
						res => {
							result.users = res.data
						}))
				break
			case 'REFUSE':
				result.desc = `流程此处将被自动驳回`
				break
		}
		if (data.userCatch[node.id] && data.userCatch[node.id].length > 0) {
			result.users = data.userCatch[node.id]
		}
		if (loadCatch) {
			data.userCatch[node.id] = result.users
		}
		return result
	}

	function getCcNode(node) {
		let result = {
			id: node.id,
			title: node.name,
			icon: 'paperplane-filled',
			name: '抄送人',
			enableEdit: node.props.shouldAdd,
			type: 'org',
			multiple: true,
			desc: node.props.shouldAdd ? '可添加抄送人' : '流程将会抄送到他们',
			users: $deepCopy(node.props.assignedUser)
		}
		if (data.userCatch[node.id] && data.userCatch[node.id].length > 0) {
			result.users = data.userCatch[node.id]
		}
		data.userCatch[node.id] = result.users
		return result
	}

	function getInclusiveNode(node, pbnode, pbid) {
		let branchTasks = {
			id: node.id,
			title: node.name,
			name: '包容分支',
			icon: 'tune-filled',
			enableEdit: false,
			active: node.branchs[0].id, //激活得分支
			options: [], //分支选项，渲染单选框
			desc: '满足条件的分支均会执行',
			branchs: {} //分支数据，不包含分支节点，key=分支子节点id，value = [后续节点]
		}
		const req = processApi.getTrueConditions({
			processDfId: props.processDefId,
			conditionNodeId: node.id,
			multiple: true,
			context: {
				...props.formData,
				deptId: props.deptId
			}
		}).then(rsp => {
			//拿到满足的条件
			const cds = new Set(rsp.data || [])
			for (let i = 0; i < node.branchs.length; i++) {
				const cdNode = node.branchs[i]
				cdNode.skip = !cds.has(cdNode.id)
				if (!cdNode.skip) {
					branchTasks.active = cdNode.id
				}
			}
			node.branchs.forEach(nd => {
				branchTasks.options.push({
					id: nd.id,
					title: nd.name,
					skip: nd.skip
				})
				branchTasks.branchs[nd.id] = []
				//设置下子级分支的父级分支节点
				data.branchNodeMap.set(nd.id, {
					node: pbnode,
					id: pbid
				})
				loadProcess(nd.children, branchTasks.branchs[nd.id], branchTasks, nd.id)
			})
		}).catch(err => {
			branchTasks.desc = `<span style="color:#CE5266;">条件解析异常，渲染失败😢<span>`
			//
		})
		data.loadingReqs.push(req)
		return branchTasks
	}

	function getConditionNode(node, pbnode, pbid) {
		let branchTasks = {
			id: node.id,
			title: node.name,
			name: '条件分支',
			icon: 'tune',
			enableEdit: false,
			active: node.branchs[0].id, //激活得分支
			options: [], //分支选项，渲染单选框
			desc: '只执行第一个满足条件的分支',
			branchs: {} //分支数据，不包含分支节点，key=分支子节点id，value = [后续节点]
		}
		const req = processApi.getTrueConditions({
			processDfId: props.processDefId,
			conditionNodeId: node.id,
			multiple: false,
			context: {
				...props.formData,
				deptId: props.deptId
			}
		}).then(rsp => {
			//拿到满足的条件
			const cds = new Set(rsp.data || [])
			for (let i = 0; i < node.branchs.length; i++) {
				const cdNode = node.branchs[i]
				cdNode.skip = !cds.has(cdNode.id)
				if (!cdNode.skip) {
					branchTasks.active = cdNode.id
				}
			}
			node.branchs.forEach(nd => {
				branchTasks.options.push({
					id: nd.id,
					title: nd.name,
					skip: nd.skip
				})
				branchTasks.branchs[nd.id] = []
				//设置下子级分支的父级分支节点
				data.branchNodeMap.set(nd.id, {
					node: pbnode,
					id: pbid
				})
				loadProcess(nd.children, branchTasks.branchs[nd.id], branchTasks, nd.id)
			})
		}).catch(err => {
			branchTasks.desc = `<span style="color:#CE5266;">条件解析异常，渲染失败😢<span>`
			//this.$err(err, "解析条件失败:")
		})
		data.loadingReqs.push(req)
		return branchTasks
	}

	function getConcurrentNode(node, pbnode, pbid) {
		let concurrentTasks = {
			id: node.id,
			title: node.name,
			name: '并行分支',
			icon: 'settings-filled',
			enableEdit: false,
			active: node.branchs[0].id, //激活得分支
			options: [], //分支选项，渲染单选框
			desc: '所有分支都将同时执行',
			branchs: {} //分支数据，不包含分支节点，key=分支子节点id，value = [后续节点]
		}
		node.branchs.forEach(nd => {
			concurrentTasks.options.push({
				id: nd.id,
				title: nd.name,
				skip: false
			})
			concurrentTasks.branchs[nd.id] = []
			//设置下子级分支的父级分支节点
			data.branchNodeMap.set(nd.id, {
				node: pbnode,
				id: pbid
			})
			loadProcess(nd.children, concurrentTasks.branchs[nd.id], concurrentTasks, nd.id)
		})
		return concurrentTasks
	}

	function selected(users) {
		_value.value[data.selectedNode.id] = []
		users.forEach(u => {
			if (data.selectedNode.users.findIndex(v => v.id === u.id) === -1) {
				u.enableEdit = true
				data.selectedNode.users.push(u)
				_value.value[data.selectedNode.id] = data.selectedNode.users
			}
		})
	}

	function getSubModel(call) {
		if (data.models) {
			call()
		} else {
			data.calls.push(call)
			if (data.calls.length === 1) {
				getGroupModels({}, true).then(rsp => {
					data.models = {}
					rsp.data.forEach(group => {
						group.items.forEach(v => data.models[v.procCode] = v.procName)
					})
					data.calls.forEach(callFun => callFun())
					data.calls.length = 0
				})
			}
		}
	}

	function addOrg(node) {
		console.log(node,"node");
		data.selectedNode = node
		orgPicker.value.show()
	}

	function doAddOrg(orgs) {
		selected(orgs)
	}

	function delOrg(i, node) {
		node.users.splice(i, 1)
	}

	//执行校验流程步骤设置
	function validate(call) {
		//遍历自选审批人节点
		let isOk = true
		console.log(isOk,'我的OK')
		for (let nodeId of data.selectUserNodes) {
			if ((_value.value[nodeId] || []).length === 0) {
				//没设置审批人员
				isOk = false
				//遍历所有的分支，从底部向上搜索进行自动切换分支渲染路线
				let brNode = data.branchNodeMap.get(nodeId)
				while (brNode && brNode.id) {
					brNode.node.active = brNode.id
					brNode = data.branchNodeMap.get(brNode.id)
				}

				nextTick(() => {
					if (instance.refs[nodeId]) {
						instance.refs[nodeId][0].errorShark()
					}
				})
				break
			}
		}
		console.log(isOk,'我的OK')
		if (call) {
			call(isOk)
		}
	}

	watch(props.formData, async () => {
		//监听表单数据变化，对流程重渲染
		processRenderDebounce()
	})
</script>

<style lang="less" scoped>
	.w-process {
		position: relative;

		.w-process-line {
			width: 2px;
			top: 20px;
			height: calc(100% - 40px);
			background-color: #9e9e9e;
			position: absolute;
			left: 13px;
		}
	}

	.w-process-render {
		.w-node-render {
			padding: 32rpx 0;
		}
	}
</style>