<template>
  <div class="app-config-manage">
    <el-dialog
      class="page-dialog config-manage-dialog"
      :visible.sync="modalIsShow"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      append-to-body
      fullscreen
      @opened="visibleChange"
    >
      <div class="part-box part-box-filter" slot="title">
        <el-form
          v-if="modalIsShow"
          ref="FilterForm"
          label-position="left"
          label-width="80px"
          :model="filter"
          :rules="rules"
          inline
        >
          <el-form-item>
            <h2 class="dialog-title">应用配置管理-{{ app.name }}</h2>
          </el-form-item>
          <el-form-item prop="versionId" label="版本">
            <version-select
              ref="VersionSelect"
              v-model="filter.versionId"
              :app-id="filter.appId"
              :clearable="false"
              manual-fetch
              select-default
              style="width: 140px"
            ></version-select>
          </el-form-item>
          <el-form-item prop="moduleId" label="模块">
            <module-select
              ref="ModuleSelect"
              v-model="filter.moduleId"
              :app-id="filter.appId"
              :clearable="false"
              manual-fetch
              select-default
            ></module-select>
          </el-form-item>
          <el-form-item label-width="0px">
            <el-button type="primary" icon="ios-search" @click="search"
              >查询</el-button
            >
            <el-button
              v-if="checkPrivilege('PROPERTY_BASELINE_EDIT', app.id)"
              type="success"
              icon="md-add"
              @click="toUpdate"
              >配置基线修改</el-button
            >
          </el-form-item>
        </el-form>
      </div>

      <div class="config-menu box-justify">
        <div class="tab-btns tab-nowrap">
          <el-button
            v-for="env in envList"
            :key="env.id"
            size="mini"
            :type="filter.envId == env.id ? 'primary' : 'default'"
            @click="selectEnv(env)"
            >{{ env.name }}
          </el-button>
        </div>
        <div class="menu-set" v-if="!IS_BASELINE">
          <el-checkbox v-model="hideReadonly">隐藏只读配置</el-checkbox>
        </div>
      </div>

      <div class="config-body">
        <div class="part-box part-box-pad config-guide">
          <ul>
            <li
              class="box-justify"
              v-for="group in groupConfigList"
              :key="group.name"
              @click="scrollTo(group)"
            >
              <p>{{ group.name | groupNameFilter }}</p>
              <span v-if="group.children.length" class="group-count">{{
                group.children.length
              }}</span>
            </li>
          </ul>
        </div>

        <div ref="ConfigTableBody" class="part-box part-box-pad config-content">
          <div
            v-for="group in groupConfigList"
            :key="group.name"
            :class="[
              'config-content-part',
              {
                'config-content-none': !group.children.length,
                'config-content-hideread': hideReadonly && !IS_BASELINE,
              },
            ]"
            :id="group.name"
          >
            <div class="config-content-title">
              <h3>
                {{ group.name | groupNameFilter }}
              </h3>
              <el-button
                v-if="checkAddPrivilege(group)"
                class="btn-success"
                type="text"
                @click="toAddGroupConfigItem(group)"
                >新增</el-button
              >
            </div>
            <div class="config-content-list">
              <div
                v-for="item in group.children"
                :key="item.key"
                :class="[
                  'config-content-item',
                  `is-${item.mode.toLowerCase()}`,
                  {
                    'is-danger':
                      item.mode === 'OVERRIDE' &&
                      isEmpty(item.value) &&
                      !IS_BASELINE,
                  },
                ]"
              >
                <div v-if="item.comment" class="config-content-comment">
                  {{ item.comment }}
                </div>
                <div class="config-content-cont">
                  <span class="cont-mode">[{{ modeMap[item.mode] }}]</span>
                  <span>{{ item.key }}</span>
                  <span>=</span>
                  <span>{{ item.value }}</span>
                  <el-button
                    v-if="checkEditPrivilege(item)"
                    class="btn-primary cont-edit"
                    type="text"
                    @click="toEdit(item)"
                    >编辑</el-button
                  >
                  <el-button
                    v-if="
                      checkEditPrivilege(item) &&
                      !item.isFromBaseline &&
                      group.name !== 'custom'
                    "
                    class="btn-danger cont-edit"
                    type="text"
                    @click="toDelete(item)"
                    >删除</el-button
                  >
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>

    <!-- UpdateAppBaseline -->
    <update-app-baseline
      v-if="checkPrivilege('PROPERTY_BASELINE_EDIT', app.id)"
      ref="UpdateAppBaseline"
      :data="filter"
      @modified="baselineModified"
    ></update-app-baseline>
    <!-- ModifyAppBaselineItem -->
    <modify-app-baseline-item
      ref="ModifyAppBaselineItem"
      :instance="curBaselineItem"
      :modes="configModes"
      @modified="baselineItemModified"
    ></modify-app-baseline-item>
    <!-- ModifyAppConfigItem -->
    <modify-app-config-item
      ref="ModifyAppConfigItem"
      :instance="curConfigItem"
      :group="curGroupForAdd"
      @modified="resetSearch"
    ></modify-app-config-item>
  </div>
</template>

<script>
import {
  appConfigBaseline,
  appConfigModes,
  appConfigGroups,
  appEnvList,
  appConfigList,
  appConfigItemDelete,
} from "../api";
import UpdateAppBaseline from "./UpdateAppBaseline.vue";
import ModifyAppBaselineItem from "./ModifyAppBaselineItem.vue";
import ModifyAppConfigItem from "./ModifyAppConfigItem.vue";
import { deepCopy } from "@/plugins/utils";

export default {
  name: "app-config-manage",
  components: { UpdateAppBaseline, ModifyAppBaselineItem, ModifyAppConfigItem },
  props: {
    app: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  filters: {
    groupNameFilter(val) {
      return val === "custom" ? "应用配置自定义" : val;
    },
  },
  data() {
    return {
      modalIsShow: false,
      filter: {
        appId: "",
        moduleId: null,
        versionId: null,
        envId: null,
      },
      searchFilter: {},
      rules: {
        moduleId: [
          {
            required: true,
            type: "number",
            message: "请选择模块",
            triggr: "change",
          },
        ],
        versionId: [
          {
            required: true,
            type: "number",
            message: "请选择版本",
            triggr: "change",
          },
        ],
      },
      configList: [],
      curConfigItem: {},
      baselineCaches: {},
      baselineList: [],
      curBaselineItem: {},
      configModes: [],
      modeMap: {},
      envList: [],
      curEnv: {},
      configGroups: [],
      curGroupForAdd: null,
      groupConfigList: [],
      hideReadonly: true,
      user: {},
    };
  },
  computed: {
    IS_BASELINE() {
      return this.filter.envId === null;
    },
  },
  created() {
    this.user = this.$ls.get("user", {});
  },
  methods: {
    isEmpty(val) {
      return val === null || val === "" || val === undefined;
    },
    async visibleChange() {
      await this.getConfigModes();
      await this.appConfigGroups();
      if (!this.filter.appId !== this.app.id) this.resetData();
      this.filter.appId = this.app.id;

      this.$nextTick(async () => {
        await this.$refs.VersionSelect.search();
        await this.$refs.ModuleSelect.search();

        await this.getEnvList();
        this.selectEnv(this.envList[0]);
      });
    },
    resetData() {
      this.filter = {
        appId: "",
        moduleId: null,
        versionId: null,
        envId: null,
      };
      this.searchFilter = {};
      this.configList = [];
      this.curConfigItem = {};
      this.baselineCaches = {};
      this.baselineList = [];
      this.curBaselineItem = {};
      this.envList = [];
      this.groupConfigList = [];
    },
    checkAddPrivilege(group) {
      const privilege = this.checkPrivilege("PROPERTY_EDIT", this.filter.envId);
      return group.name !== "custom" && !this.IS_BASELINE && privilege;
    },
    checkEditPrivilege(item) {
      const privilege = this.checkPrivilege("PROPERTY_EDIT", this.filter.envId);

      return (
        (item.mode !== "READONLY" && privilege) ||
        (this.checkPrivilege("PROPERTY_BASELINE_EDIT", this.app.id) &&
          this.IS_BASELINE)
      );
    },
    cancel() {
      this.modalIsShow = false;
    },
    open() {
      this.modalIsShow = true;
    },
    async getEnvList() {
      const res = await appEnvList({
        appId: this.app.id,
      });
      this.envList = res || [];
      this.envList.unshift({ id: null, name: "基线" });
    },
    async getConfigModes() {
      if (Object.keys(this.modeMap).length) return;

      const data = await appConfigModes();
      this.configModes = data;
      let modeMap = {};
      data.forEach(({ code, name }) => {
        modeMap[code] = name;
      });
      this.modeMap = modeMap;
    },
    async appConfigGroups() {
      if (this.configGroups.length) return;

      const data = await appConfigGroups();
      this.configGroups = data || [];
    },
    selectEnv(env) {
      this.curEnv = env;
      this.filter.envId = env.id;
      this.search();
    },
    async search() {
      const valid = await this.$refs.FilterForm.validate().catch(() => {});
      if (!valid) return;

      this.searchFilter = { ...this.filter };

      await this.resetSearch();
      this.$refs.ConfigTableBody.scrollTop = 0;
    },
    async resetSearch() {
      if (this.searchFilter.envId) {
        await this.getConfigList();
        this.buildGroupConfigList(this.configList);
      } else {
        await this.getBaselineList();
        this.buildGroupConfigList(this.baselineList);
      }
    },
    getCacheBaselineList() {
      const k = [
        this.searchFilter.appId,
        this.searchFilter.moduleId,
        this.searchFilter.versionId,
      ].join("_");
      return this.baselineCaches[k] || [];
    },
    setCacheBaselineList(baseline) {
      const k = [
        this.searchFilter.appId,
        this.searchFilter.moduleId,
        this.searchFilter.versionId,
      ].join("_");
      this.baselineCaches[k] = baseline;
    },
    async getBaselineList() {
      const cacheBaseline = this.getCacheBaselineList();
      if (cacheBaseline.length) {
        this.baselineList = cacheBaseline;
        return;
      }

      let data = await appConfigBaseline(this.searchFilter);
      data.forEach((item) => {
        item.isFromBaseline = true;
      });
      this.setCacheBaselineList(data);
      this.baselineList = data;
    },
    async getConfigList() {
      await this.getBaselineList();
      const data = await appConfigList(this.searchFilter);

      let configList = this.baselineList.map((item) => {
        let nitem = { ...item, isFromBaseline: true };
        if (item.mode === "OVERRIDE") nitem.value = null;
        nitem.refVal = item.value;
        return nitem;
      });
      // 只有配置项位于group分组内，不是应用自定义，且基线不包含此配置项时才能删除
      data.forEach((item) => {
        let bIndex = configList.findIndex((citem) => citem.key === item.key);
        if (bIndex !== -1) {
          configList[bIndex].value = item.value;
        } else {
          configList.push({ ...item, isFromBaseline: false });
        }
      });
      this.configList = configList;
    },
    buildGroupConfigList(dataList) {
      let groupConfigList = deepCopy(this.configGroups);
      groupConfigList.forEach((item) => {
        item.children = [];
      });
      const defaultAvailable = {
        type: "string",
      };

      dataList.forEach((configItem) => {
        const info = this.getConfigItemGroupPosInfo(configItem.key);
        if (info) {
          let group = groupConfigList.find(
            (gitem) => gitem.name === info.group.name
          );
          group.children.push({
            ...configItem,
            available: info.available || { ...defaultAvailable },
          });
          return;
        }

        let group = groupConfigList.find((gitem) => gitem.name === "custom");
        if (!group) {
          group = {
            name: "custom",
            prefix: "",
            children: [],
          };
          groupConfigList.unshift(group);
        }
        group.children.push({
          ...configItem,
          available: { ...defaultAvailable },
        });
      });
      // groupConfigList.sort((a, b) => b.children.length - a.children.length);

      this.groupConfigList = groupConfigList;
    },
    getConfigItemGroupPosInfo(configItemKey) {
      const curGroup = this.configGroups.find((group) =>
        configItemKey.startsWith(group.prefix + ".")
      );
      if (!curGroup) return;
      let groupData = {
        name: curGroup.name,
        prefix: curGroup.prefix,
      };

      for (let i = 0; i < curGroup.available.length; i++) {
        const avai = { ...curGroup.available[i] };
        if (avai.key.includes(".*.")) {
          const regCont = avai.key.split(".").join("\\.").replace("*", ".*");
          const reg = new RegExp(`^${regCont}$`);
          if (reg.test(configItemKey)) {
            return {
              group: groupData,
              available: avai,
            };
          }
          continue;
        }
        if (avai.key === configItemKey) {
          return {
            group: groupData,
            available: avai,
          };
        }
      }
      return {
        group: groupData,
        available: null,
      };
    },
    async toUpdate() {
      const valid = await this.$refs.FilterForm.validate().catch(() => {});
      if (!valid) return;

      this.$refs.UpdateAppBaseline.open();
    },
    toAddGroupConfigItem(group) {
      const ginfo = this.configGroups.find((item) => item.name === group.name);
      if (!ginfo) {
        this.$message.error("当前分组不存在！");
        return;
      }

      const unvalidKeys = this.configList.map((item) => item.key);
      this.curGroupForAdd = { ...ginfo, unvalidKeys };
      const groupUnvalidKeys = group.children.map((item) => item.key);
      this.curGroupForAdd.available = this.curGroupForAdd.available.filter(
        (item) => !groupUnvalidKeys.includes(item.key)
      );
      if (!this.curGroupForAdd.available.length && ginfo.available.length) {
        this.$message.error("当前分组不可添加配置项！");
        return;
      }

      this.curConfigItem = { ...this.filter, available: { type: "string" } };
      this.$refs.ModifyAppConfigItem.open();
    },
    toEdit(row) {
      if (this.filter.envId) {
        this.toEditConfigItem(row);
      } else {
        this.toEditBaseline(row);
      }
    },
    toEditBaseline(row) {
      this.curBaselineItem = { ...row, ...this.filter };
      this.$refs.ModifyAppBaselineItem.open();
    },
    toEditConfigItem(row) {
      this.curConfigItem = { ...row, ...this.filter };
      this.$refs.ModifyAppConfigItem.open();
    },
    baselineItemModified() {
      this.setCacheBaselineList([]);
      this.resetSearch();
    },
    baselineModified(data) {
      this.setCacheBaselineList(data);
      this.resetSearch();
    },
    async toDelete(item) {
      const confirm = await this.$confirm(
        `确定要删除配置项【${item.key}】吗?`,
        "提示",
        {
          type: "warning",
        }
      ).catch(() => {});
      if (confirm !== "confirm") return;
      const res = await appConfigItemDelete({
        ...this.filter,
        key: item.key,
      }).catch(() => {});
      if (!res) return;
      this.$message.success("删除成功！");
      this.resetSearch();
    },
    scrollTo(group) {
      this.$refs.ConfigTableBody.scrollTop =
        document.getElementById(group.name).offsetTop - 20;
    },
  },
};
</script>
