<template>
  <el-container class="sa-access panel-block">
    <el-main id="scrollWrap" class="sa-p-0" v-loading="state.loading">
      <template v-for="(val, level) in state.show" :key="level">
        <el-scrollbar height="100%" v-loading="val.loading">
          <div class="title sa-flex sa-row-between">
            <span>{{ val.pdata?.title }}</span>
            <slot name="add" :pdata="val.pdata">
              <template v-if="val.pdata">
                <el-checkbox
                  v-if="multiple"
                  class="sa-m-r-8"
                  v-model="val.pdata.checked"
                  :indeterminate="val.pdata.indeterminate"
                  label="全选"
                  @update:model-value="handleSelect($event, val.pdata)"
                />
              </template>
              <template v-if="type == 'list'">
                <el-button
                  v-auth="'admin.auth.access.add'"
                  class="is-link"
                  type="primary"
                  @click="onAdd(val.pdata.id, level)"
                  >+添加</el-button
                >
              </template>
            </slot>
          </div>
          <template v-if="val.data?.length > 0">
            <sa-draggable
              v-model="val.data"
              :animation="300"
              handle=".sortable-drag"
              item-key="element"
              @end="onEnd($event, level, val.pdata)"
            >
              <template #item="{ element, index }">
                <div
                  :class="['node', 'sa-flex sa-row-between', val.index == index ? 'is-active' : '']"
                  @click="onClick(element, index, level)"
                >
                  <!-- multiple -->
                  <el-checkbox
                    v-if="multiple"
                    class="sa-m-r-8"
                    v-model="element.checked"
                    :indeterminate="element.indeterminate"
                    @click.stop
                    @change="handleSelect($event, element)"
                  />
                  <slot class="label" :data="element" :level="level">
                    <div class="item sa-flex sa-row-between">
                      <div class="sa-flex">
                        <sa-svg
                          v-if="type == 'list'"
                          class="sortable-drag sa-m-r-8"
                          name="sa-round"
                        ></sa-svg>
                        <sa-icon class="icon sa-m-r-4" :icon="element.icon" size="16" />
                        <div>{{ element.title }}</div>
                      </div>
                      <div v-if="type == 'list'" class="sa-flex">
                        <el-icon
                          v-auth="'admin.auth.access.detail'"
                          class="edit sa-m-r-8"
                          @click.stop="onEdit(element.id, index, level)"
                        >
                          <Edit />
                        </el-icon>
                        <el-popconfirm
                          width="fit-content"
                          confirm-button-text="确认"
                          cancel-button-text="取消"
                          title="确认删除这条记录?"
                          @confirm="onDelete(element.id, index, level)"
                        >
                          <template #reference>
                            <el-icon
                              v-auth="'admin.auth.access.delete'"
                              class="delete sa-m-r-8"
                              @click.stop
                            >
                              <Delete />
                            </el-icon>
                          </template>
                        </el-popconfirm>
                      </div>
                    </div>
                  </slot>
                  <div class="arrow-right">
                    <el-icon v-if="!isEmpty(element.children)">
                      <ArrowRight />
                    </el-icon>
                  </div>
                </div>
              </template>
            </sa-draggable>
          </template>
          <template v-if="!val.loading && val.data.length == 0">
            <div class="empty">暂无数据</div>
          </template>
        </el-scrollbar>
      </template>
    </el-main>
  </el-container>
</template>

<script setup>
  import { nextTick, onMounted, reactive, watch } from 'vue';
  import admin from '@/app/admin/api';
  import SaDraggable from 'vuedraggable';
  import { useModal } from '@/sheep/hooks';

  import { isEmpty } from 'lodash';
  import AccessEdit from '../edit.vue';
  const emit = defineEmits(['update:modelValue']);
  const props = defineProps({
    type: String,
    isChangeParentId: Boolean,
    role_id: {
      type: [String, Number],
      default: 0,
    },
    modelValue: {
      type: Array,
      default: [],
    },
    multiple: {
      type: Boolean,
      default: false,
    },
  });

  let manualChecked = false;

  const state = reactive({
    loading: false,
    app: [],
    checkedIds: props.modelValue,
    newIds: [],
    show: {},
  });
  watch(
    () => props.modelValue,
    () => {
      state.checkedIds = props.modelValue;
    },
  );

  async function getData() {
    state.loading = true;
    const { error, data } = await admin.auth.access[props.type]({
      parent_id: 0,
      role_id: props.role_id,
    });
    if (error === 0) {
      state.app = data;

      // 初始化数据
      let appItem = initAppItem();

      initCalculate(state.app, appItem);

      // 是否选中
      await calculateShow(appItem);
    }

    state.loading = false;
  }

  function initAppItem() {
    let appItem = {
      id: 0,
      title: '应用',
      checked: true,
      indeterminate: false,
      children: state.app,
    };
    let allData = [];
    flattenData(state.app, allData);
    if (state.checkedIds.length == 0) {
      appItem.checked = false;
      appItem.indeterminate = false;
    } else {
      if (allData.length == state.checkedIds.length) {
        appItem.checked = true;
        appItem.indeterminate = false;
      } else {
        appItem.checked = false;
        appItem.indeterminate = true;
      }
    }
    return appItem;
  }

  // 初始化选中数据
  function initCalculate(data, parent = {}) {
    data.forEach((item) => {
      item.parent = parent;

      if (state.checkedIds.includes(item.id + '')) {
        if (!isEmpty(item.children)) {
          let arr = [];
          flattenData(item.children, arr);
          if (arr.every((k) => state.checkedIds.includes(k.id + ''))) {
            item.checked = true;
            item.indeterminate = false;
          } else {
            item.checked = false;
            item.indeterminate = true;
          }
        } else {
          item.checked = state.checkedIds.includes(item.id + '');
          item.indeterminate = !state.checkedIds.includes(item.id + '');
        }
      } else {
        item.checked = false;
        item.indeterminate = false;
      }

      if (!isEmpty(item.children)) {
        initCalculate(item.children, item);
      }
    });
  }

  // 扁平化数据
  function flattenData(data, arr) {
    data.forEach((item) => {
      arr.push(item);
      if (!isEmpty(item.children)) {
        flattenData(item.children, arr);
      }
    });
  }

  async function calculateShow(item, index = null, level = 0) {
    // 选中展开
    if (level != 0) {
      state.show[level].index = index;
    }

    // 清除多余数据
    for (let key in state.show) {
      if (key > level) {
        delete state.show[key];
      }
    }

    // loading
    state.show[Number(level) + 1] = {
      loading: true,
    };

    if (isEmpty(item.children)) {
      item.children = [];
    }

    state.show[Number(level) + 1] = {
      index: null,
      data: item.children,
      pdata: item,
      loading: false,
    };
  }

  async function onClick(item, index, level) {
    calculateShow(item, index, level);
    nextTick(() => {
      let left =
        document.getElementById('scrollWrap').scrollWidth -
        document.getElementById('scrollWrap').offsetWidth;
      document.getElementById('scrollWrap').scrollTo({
        top: 0,
        left: left,
        behavior: 'smooth',
      });
    });
  }

  function handleSelect(checked, item) {
    manualChecked = true;

    // 计算所有子元素
    doChecked(item, checked);

    // 计算父元素
    doCheckedParent(item);

    state.newIds = [];
    getCheckedIds(state.app);
    emit('update:modelValue', state.newIds);
  }

  function doChecked(item, checked) {
    item.checked = checked;
    item.indeterminate = false;
    if (!isEmpty(item.children)) {
      item.children.forEach((i) => {
        i.checked = checked;
        i.indeterminate = false;
        doChecked(i, checked);
      });
    }
  }

  function doCheckedParent(i) {
    // 如果有父级
    if (!isEmpty(i.parent)) {
      if (!isEmpty(i.parent.children)) {
        // 部分选中
        i.parent.checked = false;
        i.parent.indeterminate = true;

        // 全选中
        if (i.parent.children.every((k) => k.checked)) {
          i.parent.checked = true;
          i.parent.indeterminate = false;
        }

        // 未选中
        if (!i.parent.children.some((k) => k.checked || k.indeterminate)) {
          i.parent.checked = false;
          i.parent.indeterminate = false;
        }
      }
      doCheckedParent(i.parent);
    }
  }

  async function onEnd(e, level, pdata) {
    if (e.newIndex != e.oldIndex) {
      // 拖动元素修改weigh
      pdata.children[e.oldIndex].weigh = pdata.children[e.newIndex].weigh + 1;
      await admin.auth.access.edit(pdata.children[e.oldIndex].id, {
        weigh: pdata.children[e.oldIndex].weigh,
      });

      // 更新数据
      const { data } = await admin.auth.access[props.type]({
        parent_id: pdata.id,
        role_id: props.role_id,
      });
      pdata.children = data;
      state.show[level].data = data;

      if (level != 0) {
        state.show[level].index = null;
      }
      // 清除多余数据
      for (let key in state.show) {
        if (key > level) {
          delete state.show[key];
        }
      }
    }
  }
  function onAdd(id = 0, level) {
    useModal(
      AccessEdit,
      {
        title: '新建',
        type: 'add',
        parent_id: id,
      },
      {
        confirm: async () => {
          const { data } = await admin.auth.access[props.type]({
            parent_id: id,
            role_id: props.role_id,
          });
          state.show[level].data = data;
        },
      },
    );
  }
  function onEdit(id, index, level) {
    useModal(
      AccessEdit,
      {
        title: '编辑',
        type: 'edit',
        id: id,
      },
      {
        confirm: async (res) => {
          state.show[level].data[index] = res.data;
        },
      },
    );
  }

  async function onDelete(id, index, level) {
    const { error } = await admin.auth.access.delete(id);
    if (error == 0) {
      state.show[level].data.splice(index, 1);
      for (let key in state.show) {
        if (key > level) {
          delete state.show[key];
        }
      }
    }
  }

  watch(
    () => state.app,
    async () => {
      if (manualChecked) {
        state.newIds = [];
        getCheckedIds(state.app);

        emit('update:modelValue', state.newIds);
      }
    },
    {
      deep: true,
    },
  );

  watch(
    () => props.role_id,
    (newVal) => {
      if (props.isChangeParentId) {
        state.checkedIds = [];
      }
      getData();
    },
  );

  function getCheckedIds(data) {
    data.forEach((i) => {
      if (i.checked || i.indeterminate) {
        state.newIds.push(i.id + '');
      }
      if (!isEmpty(i.children)) {
        getCheckedIds(i.children);
      }
    });
  }
  onMounted(() => {
    if (props.role_id) {
      getData();
    }
  });
</script>
<style lang="scss" scoped>
  .sa-access {
    font-size: 14px;
    color: var(--sa-subtitle);
    .el-main {
      display: flex;
      transition: all 3s;
    }
    .el-scrollbar {
      flex-shrink: 0;
      width: 258px;
      border-right: 1px solid var(--sa-border);
      border-left: 1px solid var(--sa-border);
      margin: 0 8px;
      background: var(--sa-background-assist);
      &:first-of-type {
        margin-left: 0;
      }
      &:last-of-type {
        margin-right: 0;
      }
    }
    .title {
      height: 32px;
      padding: 0 16px;
      border-bottom: 1px solid var(--sa-border);
    }
    .node {
      width: inherit;
      padding: 0 16px;
      cursor: pointer;
      .item {
        flex: 1;
        height: 32px;
        display: flex;
        align-items: center;
        justify-content: space-between;
      }
      .sa-icon {
        width: 16px !important;
        height: 16px !important;
      }
      .edit {
        color: var(--el-color-primary);
      }
      .delete {
        color: var(--el-color-danger);
      }
      .arrow-right {
        width: 14px;
        height: 14px;
        display: flex;
        align-items: center;
      }
      &.is-active {
        background: var(--t-bg-active);
        color: var(--el-color-primary);
      }
    }
    .empty {
      width: inherit;
      text-align: center;
      min-height: 400px;
      line-height: 400px;
    }
  }
</style>
