<template>
  <div>
    <div v-if="!diffs" class="p-5"><d-skeleton></d-skeleton></div>
    <d-splitter v-if="diffs" class="splitter-border" style="height: 100%">
      <template v-slot:DSplitterPane>
        <d-splitter-pane
          collapseDirection="before"
          size="320px"
          minSize="320px"
          :collapsible="true"
          @size-change="setSplitterCollapse"
          @collapsed-change="collapsedChange"
        >
          <div class="pane-content">
            <div v-if="diffTree" style="min-width: 320px" class="left-tree pl-5 pr-5 pt-5">
              <d-search
                class="mb10"
                style="width: 100%"
                is-keyup-search
                placeholder="查找文件"
                :delay="100"
                @search="onSearch"
              ></d-search>
              <d-tree @node-click="onTreeClick" ref="treeRef" :data="diffTree" class="mt-2">
                <template #content="{ nodeData }">
                  <div class="ml-2" :title="nodeData?.file_path">{{ nodeData?.file_path }}</div>
                </template>
                <template #icon="{ nodeData }">
                  <span class="inline-block w-5">
                    <Icon v-if="nodeData.isLeaf" name="gt-file-c" />
                    <Icon v-else :name="nodeData.expanded ? 'gt-folder-open-c' : 'gt-folder-c'" />
                  </span>
                </template>
              </d-tree>
            </div>
          </div>
        </d-splitter-pane>
        <d-splitter-pane minSize="15%">
          <div class="pane-content">
            <div class="flex-1 pt-5 bg-[#f6f6f8]">
              <div class="flex px-5 justify-between items-center">
                <div class="flex items-center">
                  <StatusSelect v-model:value="selected" />
                  <div class="flex items-center ml-4">
                    <div class="text-[#2D2D2E] flex items-center">
                      共
                      <div class="text-[#517ca0] mx-1">{{ diffs.changes?.length }}</div>
                      个文件变更
                    </div>
                    <p class="text-green-500 ml-4">+{{ diffs.added_lines }}</p>
                    <p class="text-red-500 ml-1">-{{ diffs.removed_lines }}</p>
                  </div>
                  <span class="w-px h-5 bg-gray-300 mx-4"></span>
                  <div class="flex items-center gap-2">
                    <d-dropdown style="width: 150px" :position="['bottom-start']" align="start" close-scope="all">
                      <div class="flex items-center justify-between gap-2 cursor-pointer">
                        <div class="commitTitle text-[#707a87] hover:text-[#2d2d2e]">
                          {{ preCommitName }}
                        </div>
                        <Icon name="gt-select-arrow-down-12" size="12px"></Icon>
                      </div>
                      <template #menu>
                        <ul>
                          <li
                            class="p-2 cursor-pointer hover:bg-[#f6f6f8]"
                            v-for="item in firstCommitOptions"
                            :key="item.value"
                            :value="item.value"
                            :name="item.name"
                            @click="
                              preCommit = item.value;
                              baseLineChange();
                            "
                          >
                            <div class="px-2">
                              <div style="font-weight: 700">{{ item.name }}</div>
                              <div v-if="item.head_commit_sha">
                                {{ item.head_commit_sha.slice(0, 8) }}
                              </div>
                              <div v-if="item.created_at">{{ formatTime(item.created_at) }}</div>
                            </div>
                          </li>
                        </ul>
                      </template>
                    </d-dropdown>
                    <Icon name="gt-exit" size="12px"></Icon>
                    <d-dropdown style="width: 150px" :position="['bottom-start']" align="start">
                      <div class="flex items-center justify-between gap-2 cursor-pointer">
                        <div class="commitTitle text-[#707a87] hover:text-[#2d2d2e]">
                          {{ afterCommitName }}
                        </div>
                        <Icon name="gt-select-arrow-down-12" size="12px"></Icon>
                      </div>
                      <template #menu>
                        <ul>
                          <li
                            class="p-2 cursor-pointer hover:bg-[#f6f6f8]"
                            v-for="item in lastCommitOptions"
                            :key="item.value"
                            :value="item.value"
                            :name="item.name"
                            @click="
                              afterCommit = item.value;
                              baseLineChange();
                            "
                          >
                            <div class="px-2">
                              <div style="font-weight: 700">{{ item.name }}</div>
                              <div v-if="item.head_commit_sha">
                                {{ item.head_commit_sha.slice(0, 8) }}
                              </div>
                              <div v-if="item.created_at">{{ formatTime(item.created_at) }}</div>
                            </div>
                          </li>
                        </ul>
                      </template>
                    </d-dropdown>
                  </div>
                  <div class="ml-4"></div>
                </div>
                <div class="flex items-center">
                  <d-popover content="展开/收起全部文件" trigger="hover" :position="['top']">
                    <span class="flex items-center cursor-pointer" @click="handleExpand">
                      <Icon name="gt-frame-expand2"></Icon>
                    </span>
                  </d-popover>
                  <span class="w-px h-5 bg-gray-300 mx-3"></span>
                  <d-popover content="设置" trigger="hover" :position="['top']">
                    <ShowFormat />
                  </d-popover>
                </div>
              </div>
              <div class="mt-4 flex">
                <div v-if="diffsloading" class="p-5 h-80 w-[100%]"><d-skeleton></d-skeleton></div>
                <DataPanel
                  class="filter-diff-list"
                  :loading="diffsloading"
                  :empty="!filterDiffs?.length"
                  :skeleton="diffsloading"
                  :card="false"
                >
                  <div class="flex-1 codeblock p-5 pt-0" :key="`${preCommit}-${afterCommit}`" v-show="!diffsloading">
                    <div
                      :class="{ selectItem: selectedFile === item.file_path }"
                      class="mb-3 overflow-auto rounded-[8px]"
                      style="box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.09)"
                      v-for="(item, index) in filterDiffs"
                      :key="item.file_path"
                      @click="codelistClick(item.file_path)"
                    >
                      <DiffView
                        :index="index"
                        :repoId="repoId"
                        :diffObj="item"
                        :discussions="diss?.length ? diss.find((x) => x.path === item.file_path) : null"
                        :showIssue="true"
                        :allowComment="canComment"
                        :showViewSource="true"
                        :showReadBtn="true"
                        @onMergeAble="$emit('onMergeAble')"
                        @foldChange="(e) => (item.fold = e)"
                      />
                    </div>
                  </div>
                </DataPanel>
              </div>
            </div>
          </div>
        </d-splitter-pane>
      </template>
    </d-splitter>
    <!-- 勿删 Status 枚举需要 -->
    <div
      class="hidden text-[#fa9841] bg-[#fa984133] bg-[#3ac29533] bg-[#f66f6a33] bg-[#71757f33] text-[#3ac295] text-[#f66f6a] text-[#71757f]"
    ></div>
  </div>
</template>
<script setup lang="ts">
import { computed, ref, toValue, watch, reactive, onMounted, provide, inject } from 'vue';
import { useReq } from '@/utils/hooks/useReq';
import { changes } from '@/api/merge/index';
import ShowFormat from './ShowFormat.vue';
import StatusSelect from './StatusSelect.vue';
import { changesTrees, versions, getDis } from '@/api/merge/index';
import DiffView from './DiffView.vue';
import { useAccountStore } from '@/stores/user';
import { escapeResData } from '@/utils';
import { diffFilter } from './support';
import { repoInfoStore } from '@/stores/Repo';
import { useMrChangeStore } from '@/stores/merge';
import { formatTime } from './support';

const mrChangeStore = useMrChangeStore();
const user = useAccountStore();
const accountInfo = user.accountInfo;
const arts_id = accountInfo?.arts_id;
const { isPrivate, isVisitor } = repoInfoStore();
const canComment = computed(() => {
  if (isPrivate) return isVisitor;
  else return !!arts_id;
});

//
const repoId = inject('repoId');
const iid = inject('iid');
provide('arts_id', arts_id);
provide('accountInfo', accountInfo);
const mrInfo = inject('mrInfo');
provide('source_branch', mrInfo?.value?.source_branch);

// 筛选
const selected = ref([]);
const keyWord = ref('');
const allFold = ref(false);
const handleExpand = () => {
  diffs?.value?.changes?.forEach((x) => (x.collapsed = allFold.value));
  allFold.value = !allFold.value;
};

// 基线
const preCommit = ref(null);
const afterCommit = ref(null);
provide('preCommit', preCommit);
provide('afterCommit', afterCommit);
const preCommitName = computed(() => {
  return commitOptions?.value?.find((x) => x.value === preCommit.value)?.name || '';
});
const afterCommitName = computed(() => {
  return commitOptions?.value?.find((x) => x.value === afterCommit.value)?.name || '';
});

const from_diff_id = computed(() => {
  if (!commitOptions?.value?.length) return null;
  if (preCommit.value === -1 && afterCommit.value === commitOptions.value[0].value) return null;
  return afterCommit.value;
});
const to_diff_id = computed(() => {
  if (preCommit.value === -1) return null;
  return preCommit.value;
});
const { data: commitOptions } = useReq(versions, reactive({ repoId, iid }), null, (res) => {
  const data = res.map((x, i) => ({ ...x, value: x.id }));
  for (let i = 0; i < data.length; i++) {
    if (i === 0) data[i].name = '最新版本';
    else data[i].name = '版本' + (data.length - i);
  }
  return [...data, { value: -1, name: '基线' }];
});
watch(commitOptions, () => {
  if (!commitOptions.value?.length) return;
  //
  preCommit.value = -1;
  afterCommit.value = commitOptions.value[0].value;
});
const firstCommitOptions = computed(() => {
  if (!commitOptions.value?.length) return [];
  const firstOptions = commitOptions.value.filter((x) => x.value < afterCommit.value);
  return [...firstOptions];
});
const lastCommitOptions = computed(() => {
  if (!preCommit.value || !commitOptions.value?.length) return [];
  const afterOptions = commitOptions.value.filter((x) => x.value > preCommit.value);
  return [...afterOptions];
});
const baseLineDateNow = ref(+new Date());
const baseLineChange = () => {
  loadDiffTree();
  loadDiff();
  baseLineDateNow.value = +new Date();
};

// diffs files tree
const formatTree = (childrenProp, pickProps) => (sourceList) => {
  if (!Array.isArray(sourceList)) return sourceList;
  return sourceList.map((node) => {
    return {
      ...node,
      children: formatTree(childrenProp, pickProps)(node[childrenProp]),
      ...pickProps(node)
    };
  });
};
const findTreeNode = (id, nodes) => {
  for (const node of nodes) {
    if (node.id === id) return node;
    if (node.children?.length) {
      const deepRes = findTreeNode(id, node.children);
      if (deepRes) return deepRes;
    }
  }
  return null;
};
const diffTree = ref(null);
const loadDiffTree = () => {
  changesTrees(
    reactive({
      repoId,
      iid,
      view: 'simple',
      from_diff_id,
      to_diff_id
    })
  )
    .then((res) => {
      const data = escapeResData(res);
      const pickProps = (node) => ({
        label: node.title,
        id: node.file_path,
        expanded: true,
        selected: false
      });
      diffTree.value = formatTree('items', pickProps)(data.tree);
    })
    .catch((e) => {});
};
//
const treeRef = ref();
const onSearch = (value) => {
  treeRef.value.treeFactory.searchTree(value, { isFilter: true });
};
const selectedFile = ref('');
const onTreeClick = (node) => {
  selectedFile.value = node?.file_path;
  const thenode = diffs?.value?.changes?.find((x) => x.file_path === selectedFile.value);
  if (thenode) thenode.fold = false;

  if (node.childNodeCount > 0) {
    node.expanded = !node.expanded;
  }
  //
};
const codelistClick = (file_path) => {
  selectedFile.value = file_path;
  //
  const thenode = findTreeNode(file_path, diffTree.value);
  //
  treeRef.value.treeFactory.selectNode(thenode);
};

// diffs (文件)
const diffs = ref();
const diffsloading = ref(false);
const loadDiff = () => {
  diffsloading.value = true;
  const diffsParams = reactive({
    repoId,
    iid,
    view: 'simple',
    ignore_whitespace_change: mrChangeStore.ignore_whitespace_change,
    from_diff_id,
    to_diff_id
  });
  changes(diffsParams)
    .then((res) => {
      diffsloading.value = false;
      const data = escapeResData(res);
      diffs.value = data;
      //
    })
    .catch((e) => {
      diffsloading.value = false;
    });
};
const filterDiffs = computed(() => {
  if (!diffs?.value?.changes) return [];
  return diffs.value.changes?.filter((m) => diffFilter(m, selected));
});
//

// diss
const { data: diss } = useReq(getDis, reactive({ repoId, iid }));

watch(
  () => mrChangeStore.ignore_whitespace_change,
  () => {
    diffs.value = [];
    loadDiff();
  }
);

onMounted(() => {
  loadDiffTree();
  loadDiff();
});

const collapsedChange = (e) => {
  if (e) setSplitterCollapse('4px');
  else setSplitterCollapse('320px');
};
const setSplitterCollapse = (left) => {
  setTimeout(() => {
    document.querySelector('.devui-splitter__collapse').style.left = left;
  }, 0);
};
</script>

<style scoped lang="scss">
@import 'devui-theme/styles-var/devui-var.scss';
.codeblock {
  // background-color: $devui-global-bg;
  min-height: calc(100vh - 144px - 104px - 52px);
}

.selectItem {
  :deep(.devui-code-review__header) {
    background-image: linear-gradient(to right, rgba(103, 136, 242, 0.2), transparent);
  }
}

.left-tree {
  border-right: 1px solid $devui-dividing-line;
  position: sticky;
  top: 10px;

  min-height: calc(100vh - 144px - 104px);
}

.splitter-border {
  :deep(.resizable .devui-splitter__collapse) {
    position: fixed;
    left: 320px;
    top: 50%;
  }
  :deep(.none-resizable .devui-splitter__collapse) {
    position: fixed;
    left: 4px;
    top: 50%;
  }
}

.commitTitle {
  font-size: 14px;
  font-weight: 400;
}

.filter-diff-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
</style>
