<template>
	<!-- 组件根容器 -->
	<div class="cy-tree-select">
		<!-- 弹出层：点击下方 reference 区域后展示树形选择面板 -->
		<el-popover v-model="popoverOpen" placement="bottom-start" width="300" trigger="click" :popper-class="popperClass" @show="popoverOpen = true" @hide="popoverOpen = false">
			<!-- 弹出层内容容器 -->
			<div class="cy-tree-select-select-con">
				<!-- 搜索过滤输入框（filterable 为 true 时显示） -->
				<div v-if="filterable" class="cy-tree-select-select-con-s">
					<el-input
						v-model="filterText"
						placeholder="搜索"
						prefix-icon="el-icon-search"
						clearable
						size="small"
					></el-input>
				</div>
				<!-- 父子节点联动开关（多选且 showCheckStrictly 为 true 时显示） -->
				<div v-if="showCheckStrictly && multiple" class="cy-tree-select-select-con-t">
					<el-checkbox v-model="isCheckStrictly">父子节点联动</el-checkbox>
				</div>

				<!-- 树形选择器主体 -->
				<div class="cy-tree-select-select-con-c">
					<el-tree
						ref="tree"
						:data="treeOptions"
						:show-checkbox="multiple"
						:node-key="treeFieldMap.id"
						:check-strictly="!isCheckStrictly"
						:props="treeFieldMap"
						:expand-on-click-node="false"
						:default-expand-all="defaultExpandAll"
						:filter-node-method="filterNode"
						@check="treeCheck"
						@node-click="treeClick"
					></el-tree>
				</div>
			</div>

			<!-- 触发区域：页面上显示已选内容，点击后展开弹出层 -->
			<div slot="reference" class="cy-tree-select-select" :class="size" :style="vStyle">
				<div class="cy-tree-select-select-v">
					<!-- 未选中时显示占位提示，选中后显示已选节点 label 拼接文本 -->
					<span v-if="checkedArr.length < 1" class="cy-tree-select-select-v-placeholder">{{ placeholder }}</span>
					<span v-else>{{ checkedArr.map(item => item[treeFieldMap.label]).join() }}</span>
				</div>

				<!-- 右侧图标区：hover 显示清空按钮，否则显示下拉箭头 -->
				<div class="cy-tree-select-select-v-icon" @mouseenter="isClearIcon = true" @mouseleave="isClearIcon = false">
					<i v-if="isClearIcon && clearable" class="el-icon-circle-close" @click.stop="handleClear"></i>
					<i v-else class="el-icon-arrow-down cy-tree-select-select-v-arrow" :class="{ 'cy-tree-select-select-v-arrow-show': popoverOpen }"></i>
				</div>
			</div>
		</el-popover>
	</div>
</template>

<script>
// 引入 Element UI 底层组件，避免走全量注册
import ElCheckbox from '../../../checkbox';
import ElPopover from '../../../popover';
import ElInput from '../../../input';
import ElTree from '../../../tree';

export default {
	name: 'CyTreeSelect',
	components: {
		ElCheckbox,
		ElPopover,
		ElInput,
		ElTree
	},
	// 自定义 v-model：使用 value 作为 prop，change 作为回调事件
	model: {
		prop: 'value',
		event: 'change'
	},
	props: {
		// 绑定值：单选时为节点 id（String/Number），多选时为 id 数组
		value: {
			type: [String, Number, Array],
			default: ''
		},

		// 树形数据源（children 嵌套结构）
		options: {
			type: Array,
			default: []
		},

		// 未选中时的占位提示文本
		placeholder: {
			type: String,
			default: ''
		},

		// 组件尺寸（medium / small / mini）
		size: {
			type: String
		},

		// 是否显示搜索过滤输入框
		filterable: {
			type: Boolean,
			default: false
		},

		// 是否显示「父子节点联动」开关
		showCheckStrictly: {
			type: Boolean,
			default: true
		},

		// 父子节点是否互相关联（默认联动）
		checkStrictly: {
			type: Boolean,
			default: true
		},

		// 是否显示清空按钮
		clearable: {
			type: Boolean,
			default: false
		},

		// 是否多选模式（多选时树节点带 checkbox）
		multiple: {
			type: Boolean,
			default: false
		},

		// 树节点字段映射配置：指定 id / label / children 对应的字段名
		defaultOptions: {
			type: Object,
			default: () => {
				return {
					children: 'children',
					label: 'label',
					id: 'id'
				};
			}
		},

		// 触发区域（reference）的自定义样式
		vStyle: {
			type: [Array, String, Object],
			default: ''
		},

		// 弹出层自定义类名
		popperClass: {
			type: String,
			default: ''
		},

		// 是否默认展开所有节点
		defaultExpandAll: {
			type: Boolean,
			default: false
		}
	},
	data() {
		return {
			// 当前是否父子联动（受 checkStrictly 控制）
			isCheckStrictly: true,
			// 弹出层是否展开
			popoverOpen: false,
			// 鼠标是否悬浮在图标区（控制清空/箭头图标切换）
			isClearIcon: false,

			// 原始树数据（来自 options）
			treeOptions: [],
			// 扁平化后的树数据（便于按 id 反查节点）
			treeArr: [],
			// 搜索关键字
			filterText: '',
			// 搜索命中的节点 id 集合
			matchedNodeIds: []
		};
	},
	computed: {
		/**
		 * 字段映射配置：将用户传入的 defaultOptions 与默认值合并，
		 * 保证 children / label / id 三个 key 始终存在，
		 * 避免只传部分字段时取到的 key 为 undefined 而导致层级/选中/搜索失效
		 * @returns {Object} 合并后的字段映射
		 */
		treeFieldMap() {
			return Object.assign({ children: 'children', label: 'label', id: 'id' }, this.defaultOptions);
		},

		/**
		 * 根据 value 反查当前选中节点对象数组，用于回显文本
		 * @returns {Array} 选中节点对象数组
		 */
		checkedArr() {
			if (this.multiple) {
				let arr = [];
				this.value && this.value.forEach(id => {
					let cItem = this.treeArr.filter(tItem => tItem[this.treeFieldMap.id] === id)[0];
					if (cItem) arr.push(cItem);
				});
				return arr;
			} else {
				let cItem = this.treeArr.filter(tItem => tItem[this.treeFieldMap.id] === Number(this.value))[0];
				return cItem ? [cItem] : [];
			}
		}
	},
	watch: {
		// 监听 checkStrictly：同步到内部状态 isCheckStrictly
		checkStrictly: {
			immediate: true,
			handler() {
				this.isCheckStrictly = this.checkStrictly;
			}
		},

		// 监听 value：变化时重新设置树节点的勾选状态
		value: {
			immediate: true,
			deep: true,
			handler() {
				this.$nextTick(() => {
					this.init();
				});
			}
		},

		// 监听 options：数据变化时重新扁平化
		options: {
			deep: true,
			immediate: true,
			handler() {
				this.treeOptions = this.options;
				this.treeArr = this.turnFormatTreeData([], this.treeOptions, 0);
			}
		},

		// 监听搜索关键字：过滤树节点并自动展开命中节点
		filterText(val) {
			if (val) {
				this.matchedNodeIds = this.findMatchedNodes(val, this.treeOptions);
				this.$refs.tree.filter(val);
				this.$nextTick(() => {
					this.expandMatchedNodes();
				});
			} else {
				this.matchedNodeIds = [];
				this.$refs.tree.filter(val);
			}
		}
	},
	created() {

	},
	methods: {
		/**
		 * 树节点过滤方法：根据命中 id 集合决定节点是否显示
		 * @param {*} value - 搜索关键字
		 * @param {Object} data - 当前树节点数据
		 * @returns {boolean}
		 */
		filterNode(value, data) {
			if (!value) return true;
			return this.matchedNodeIds.includes(data[this.treeFieldMap.id]);
		},

		/**
		 * 递归查找名称包含关键字的节点 id（父节点命中则连带子节点）
		 * @param {*} value - 搜索关键字
		 * @param {Array} nodes - 待查找的树节点数组
		 * @param {boolean} [parentMatched=false] - 父节点是否已命中
		 * @returns {Array<number>} 命中节点 id 集合
		 */
		findMatchedNodes(value, nodes, parentMatched = false) {
			let matchedIds = [];

			nodes.forEach(node => {
				const currentNodeMatched = node[this.treeFieldMap.label].indexOf(value) !== -1;
				const shouldShow = currentNodeMatched || parentMatched;

				if (shouldShow) {
					matchedIds.push(node[this.treeFieldMap.id]);
				}

				if (node[this.treeFieldMap.children] && node[this.treeFieldMap.children].length > 0) {
					const childIds = this.findMatchedNodes(value, node[this.treeFieldMap.children], shouldShow);
					matchedIds = matchedIds.concat(childIds);
				}
			});

			return matchedIds;
		},

		/**
		 * 将搜索命中的节点自动展开
		 * @returns {void}
		 */
		expandMatchedNodes() {
			this.matchedNodeIds.forEach(nodeId => {
				const treeNode = this.$refs.tree.store.nodesMap[nodeId];
				if (treeNode) {
					treeNode.expand();
				}
			});
		},

		/**
		 * 根据 value 初始化树节点勾选状态（单选/多选分别处理）
		 * @returns {void}
		 */
		init() {
			let arr = [];
			if (this.multiple) {
				arr = this.value ? this.value : [];
			} else {
				arr = this.value ? [this.value] : [];
			}
			this.$nextTick(() => {
				this.$refs.tree.setCheckedKeys(arr);
			});
		},

		/**
		 * 多选时勾选变化回调：向外 emit 选中 id 数组及节点数组
		 * @param {*} value - el-tree 回调的勾选节点数据
		 * @param checkedKeys
		 * @param {Object} checkedNodes - 选中节点对象数组
		 * @fires {Event} change
		 * @returns {void}
		 */
		treeCheck(value, { checkedKeys, checkedNodes }) {
			let arr = [];
			arr = checkedNodes;
			let idArr = arr.map(item => item[this.treeFieldMap.id]);
			this.$emit('change', this.multiple ? idArr : idArr[0], this.multiple ? arr : arr[0]);
		},

		/**
		 * 单选时点击节点回调：直接选中并关闭弹出层
		 * @param {Object} value - 当前点击的树节点对象
		 * @param {Object} node - 节点对象
		 * @param {Object} event - 点击事件对象
		 * @fires {Event} change
		 * @returns {void}
		 */
		treeClick(value, node, event) {
			if (this.multiple) return;

			this.$emit('change', value[this.treeFieldMap.id], value);
			this.popoverOpen = false;
		},

		/**
		 * 清空选择：emit 空值并清空树勾选
		 * @fires {Event} change
		 * @returns {void}
		 */
		handleClear() {
			this.$emit('change', this.multiple ? [] : '', this.multiple ? [] : null);
			this.$refs.tree.setCheckedKeys([]);
		},

		/**
		 * 将嵌套树数据拍平为一维数组，并补充父节点 id（pId）便于反查
		 * @param {Array} arr - 输出的一维数组（原地收集）
		 * @param {Array} data - 当前层级的树节点数组
		 * @param {number} pId - 父节点 id
		 * @returns {Array} 拍平后的一维节点数组
		 */
		turnFormatTreeData(arr, data, pId) {
			data.forEach(item => {
				let cItem = JSON.parse(JSON.stringify(item));
				delete cItem[this.treeFieldMap.children];
				cItem.pId = pId;
				arr.push(cItem);

				if (item[this.treeFieldMap.children] && item[this.treeFieldMap.children].length > 0) {
					this.turnFormatTreeData(arr, item[this.treeFieldMap.children], item[this.treeFieldMap.id]);
				}
			});
			return arr;
		}
	}
};
</script>
