<template>
  <div class="demo-tree-select-department-tree">
    <department-tree
      v-model="value"
      :data="treeData"></department-tree>
  </div>
</template>
<script>
import DepartmentTreeData from "./department-tree.json"

import isEqual from "lodash/isEqual"
import differenceWith from "lodash/differenceWith"
import difference from "lodash/difference"
import unionWith from "lodash/unionWith"
import union from "lodash/union"
import xor from "lodash/xor"

import { TreeSelect } from "amumu-ui"

const DepartmentTree = {
  render() {
    return (
      <TreeSelect
        value={this.localValue}
        onInput={this.syncHandler}
        tree-data={this.treeData.tree}
        render-content={this.renderContent}
        tree-checkable={true}
        filter-selector-value={this.filterSelectorValue}
        tree-default-expanded-keys={[0]}
      />
    )
  },
  components: {
    TreeSelect,
  },
  model: {
    event: "sync",
    prop: "value",
  },
  props: {
    data: {
      type: Array,
      default: () => [],
    },
    value: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      localValue: [],
      realValue: [],
    }
  },
  computed: {
    treeData() {
      const processTreeData = data => {
        const usersMap = {}
        const loop = tree => {
          return tree.map(treeNode => {
            let members = []
            let nodes = []

            if (treeNode.member) {
              members = treeNode.member.map(item => {
                usersMap[item.user_id] = (usersMap[item.user_id] || []).concat(treeNode.id)
                return {
                  user: true,
                  label: item.nickname || item.real_name,
                  value: this.joinId(item.user_id)(treeNode.id),
                  user_id: item.user_id,
                }
              })
            }

            if (treeNode.node) {
              nodes = loop(treeNode.node)
            }

            return {
              department: true,
              label: treeNode.name,
              value: treeNode.id,
              children: nodes.concat(members),
            }
          })
        }
        return {
          tree: loop(data),
          usersMap,
        }
      }

      return processTreeData(this.data)
    },
  },
  watch: {
    value: {
      immediate: true,
      handler(newValue, oldValue) {
        if (xor(newValue, oldValue).length > 0) {
          this.localValue = newValue.reduce((result, userId) => {
            return result.concat(this.treeData.usersMap[userId].map(this.joinId(userId)))
          }, [])
        }
      },
    },
    realValue(newValue, oldValue) {
      if (xor(newValue, oldValue).length > 0) {
        this.$emit("sync", newValue)
      }
    },
    localValue(newValue, oldValue) {
      const usersMap = this.treeData.usersMap

      const add = differenceWith(newValue, oldValue, isEqual)
      const multiParentUserInAdd = add.filter(id => {
        const userId = this.getUserId(id)
        return usersMap[userId].length > 1
      })

      if (multiParentUserInAdd.length > 0) {
        const users = multiParentUserInAdd.reduce((result, id) => {
          const [parentId, userId] = this.splitId(id)
          result[userId] = (result[userId] || []).concat(parentId)
          return result
        }, {})

        const needFill = Object.keys(users).some(
          userId => usersMap[userId].length !== users[userId].length
        )
        if (needFill) {
          const addList = Object.keys(users).reduce((result, userId) => {
            return result.concat(
              difference(usersMap[userId], users[userId]).map(this.joinId(userId))
            )
          }, [])

          this.localValue = this.localValue.concat(addList)
        }
      }

      const del = differenceWith(oldValue, newValue, isEqual)
      const multiParentUserInDel = del.filter(id => {
        const userId = this.getUserId(id)
        return usersMap[userId].length > 1
      })

      if (multiParentUserInDel.length > 0) {
        const removeList = multiParentUserInDel.map(this.getUserId)
        this.localValue = this.localValue.filter(id => !removeList.includes(this.getUserId(id)))
      }

      this.realValue = union(this.localValue.map(this.getUserId))
    },
  },
  methods: {
    joinId(userId) {
      return parentId => `${parentId}:${userId}`
    },
    splitId(id) {
      return id.split(":")
    },
    getUserId(id) {
      return this.splitId(id)[1]
    },
    renderContent(h, { node }) {
      return (
        <div>
          {node.data.department ? "部门" : "成员"}:{node.label}
        </div>
      )
    },
    filterSelectorValue(selectorValueList) {
      return unionWith(selectorValueList, (a, b) => {
        return this.getUserId(a.value) === this.getUserId(b.value)
      })
    },
    syncHandler(value) {
      this.localValue = value
    },
  },
}

export default {
  components: {
    DepartmentTree,
  },
  data() {
    return {
      value: [],
      treeData: Object.freeze([DepartmentTreeData]),
    }
  },
}
</script>
