<template>
  <el-dialog :title="$t('Project conflict resolver')" width="80vw" :visible.sync="visible" @opened="onOpen"
             @close="onClose"
             :close-on-click-modal="false"
             :append-to-body="true"
             custom-class="flex-dialog project-conflict-resolve-editor"
  >
    <div class="wrapper">
      <span>冲突数：{{ conflictCounting }}</span>
      <el-tabs tab-position="left" class="category-tab">
        <el-tab-pane v-for="(categoryConfig, category) in categories" :key="category"
                     :label="`${categoryConfig.label}(${conflictCountingMap[category]})`">
          <list-compare-view class="compare-view" :compares="compareGroup[category]" :category="category"
                             @merge-conflict="mergeConflict"/>
        </el-tab-pane>
      </el-tabs>
    </div>
    <div slot="footer" class="dialog-footer">
      <div>

      </div>
      <div>
        <el-button size="mini" @click="onCancel">{{ $t('Cancel') }}</el-button>
        <el-button size="mini" @click="onSave" type="primary" :disabled="conflictCounting > 0">{{ $t('Save') }}
        </el-button>
      </div>
    </div>
    <code-conflict-resolve-dialog ref="codeConflictResolveDialog" @resolved="onCodeConflictResolved"/>
  </el-dialog>
</template>

<script>
import ListCompareView from "./ProjectConflictResolveDialog/ListCompareView";
import CodeConflictResolveDialog from "./CodeConflictResolveDialog";

export default {
  name: "ProjectConflictResolveDialog",
  components: {CodeConflictResolveDialog, ListCompareView},
  data() {
    let categories = this.$t('categories');
    let conflictCountingMap = {};
    for (let key in categories) {
      conflictCountingMap[key] = 0;
    }
    return {
      visible: false,
      categories,
      compareGroup: {},
      conflictCountingMap,
    }
  },
  computed: {
    conflictCounting() {
      let t = 0;
      for (let count of Object.values(this.conflictCountingMap)) {
        t += count;
      }
      return t;
    }
  },
  methods: {
    show(remoteData, localData) {
      try {
        this.analyseCompare(remoteData, localData);
      } catch (e) {
        console.log(e);
      }

      this.visible = true;
    },
    onClose() {

    },
    onOpen() {

    },
    async onSave() {
      await this.$confirm(this.$t('All conflict has been resolved'), this.$t('Alert'), {
        confirmButtonText: this.$t('Confirm'),
        cancelButtonText: this.$t('Cancel'),
        type: 'warning'
      }).then(() => {
        this.step++;
        this.visible = false;
        this.$emit('resolved', JSON.stringify(this.localObj));
      }).catch((e) => {
      });
      this.visible = false;
    },
    onCancel() {

      this.visible = false;
    },
    analyseCompare(remoteData, localData) {
      let remoteJson = remoteData ? JSON.parse(remoteData) : {};
      let localJson = JSON.parse(localData);

      this.localObj = localJson;
      let that = this;

      for (let key in this.categories) {
        this.conflictCountingMap[key] = 0;
      }

      let compareGroup = {};
      for (let category in this.categories) {
        let categoryConfig = this.categories[category];
        let compares = [];

        if (categoryConfig.simple) {
          addCompare(category, compares, remoteJson[category], localJson[category]);
        } else {
          let {key, name} = categoryConfig;
          let remote = remoteJson[category] || [];
          let local = localJson[category] || [];

          let analysedUUIDs = [];
          for (let remoteItem of remote) {
            const localItem = local.find(item => item[key] === remoteItem[key]);
            addCompare(category, compares, remoteItem, localItem, remoteItem[key], name);
            analysedUUIDs.push(remoteItem[key]);
          }
          for (let localItem of local) {
            if (analysedUUIDs.includes(localItem[key])) {
              continue;
            }
            const remoteItem = remote.find(item => item[key] === localItem[key]);
            addCompare(category, compares, remoteItem, localItem, localItem[key], name);
            analysedUUIDs.push(localItem[key]);
          }
        }
        compareGroup[category] = compares;
      }

      this.compareGroup = compareGroup;

      function addCompare(category, compares, remote, local, key, name = 'name') {
        let remoteStr = remote ? JSON.stringify(remote) : '';
        let localStr = local ? JSON.stringify(local) : '';
        let compare = {
          key,
          resolved: !((!remote || !local) || remoteStr !== localStr),
        };
        if (remote) {
          compare.remote = {name: remote[name], data: remoteStr, obj: remote};
        }
        if (local) {
          compare.local = {name: local[name], data: localStr, obj: local};
        }

        compares.push(compare);

        if (!compare.resolved) {
          that.conflictCountingMap[category]++;
        }
      }
    },
    mergeConflict(action, conflict, category) {
      const {remote, local} = conflict;

      switch (action) {
        case 'remote':
          this.reSaveConflict(conflict, remote, category);
          break;
        case 'local':
          this.reSaveConflict(conflict, local, category);
          break;
        case 'manual':
          this.lastConflict = conflict;
          this.lastCategory = category;
          this.$refs.codeConflictResolveDialog.show(remote ? remote.data : '', local ? local.data : '');
          break;
      }
    },
    onCodeConflictResolved(data) {
      if (this.lastConflict) {
        this.reSaveConflict(this.lastConflict, {obj: data}, this.lastCategory);
      }
    },
    reSaveConflict(conflict, result, category) {
      let resultData;
      if (result) {
        if (typeof result.obj === 'string') {
          resultData = JSON.parse(result.obj);
        } else {
          resultData = result.obj;
        }
      }
      let data = this.localObj[category];
      let categoryConfig = this.categories[category];
      if (categoryConfig.simple) {
        if (resultData) {
          this.localObj[category] = resultData;
        } else {
          delete this.localObj[category];
        }
      } else {
        let matched = false;
        data.some((item, index) => {
          if (item[categoryConfig.key] === conflict.key) {
            if (resultData) {
              data[index] = resultData;
            } else {
              data.splice(index, 1);
            }
            matched = true;
            return true;
          }
        });
        if (!matched) {
          if (resultData) {
            data.push(resultData);
          } else {

          }
        }
      }
      this.$set(conflict, 'resolved', true);
      this.conflictCountingMap[category]--;
    }
  }
}
</script>

<style scoped>

</style>
