<script setup lang="ts">
import { computed, onMounted, ref, type Ref } from "vue";
import G6, { type Graph, type Item, type NodeConfig } from '@antv/g6';
import type { SearchResult } from "@/types/search.types";
import type { Request } from "@/types/knowledgeGraph.types";
import SearchResultList from "../search/components/SearchResultList.vue";
import PropertyComponent from "./components/PropertyComponent.vue";
import * as knowledgeGraphService from "@/services/knowledgeGraph.service"
import * as searchService from "@/services/search.service"
import { GraphDataManager } from "@/models/knowledgeGraph.model";
import { message } from "ant-design-vue";
import SpinComponent from "@/components/SpinComponent.vue";

// 搜索结果
const searchResult: Ref<SearchResult | null> = ref(null);
const currentPage = ref(1)
const searchLoading = ref(false)
const searchNodeName = ref<string | null>(null)
// 节点搜索统计
const propertyData = computed(() => {
  return {
    name: searchNodeName.value || '',
    docCount: searchResult.value?.total || 0,
    keywords: searchResult.value?.aggs["KW"] || [],
  }
})

const keyword = ref('建筑工程 工程设计');

let graph: Graph | null = null;

const graphDataManager = new GraphDataManager();

function find(request: Request) {
  const msgHandler = message.loading("正在查找关联节点...", 60)
  knowledgeGraphService.find(request).then((nodes) => {
    graphDataManager.addSubjectNodes(nodes);
    if (graph && nodes.length > 0) {
      graph.read(graphDataManager.getGraphData())
    }
    msgHandler()
  }).catch(() => {
    msgHandler()
  })
}

function findParents(id: number) {
  find({name: '', id, size: 10, findParents: true, findChildren: false})
}

function findChildren(id: number) {
  find({name: '', id, size: 10, findParents: false, findChildren: true})
}

function findName(name: string) {
  find({name, size: 10, findParents: false, findChildren: true})
}

function searchSubject(name: string) {
  searchLoading.value = true
  const from = Math.max(currentPage.value - 1, 0) * 10
  const request = {
    "query": `TP=(${name})`,
    from,
    size: 10,
    aggs: {
      "KW": {
        "sort": "count/desc"
      }
    }
  }
  searchService.search(request).then((resp) => {
    searchResult.value = resp;
    searchLoading.value = false
  }).catch(() => {
    message.warning('搜索学科分类遇到问题，请稍后再试');
    searchLoading.value = false
  })
}

function onPaginationChange(p: number) {
  currentPage.value = p
  if ( searchNodeName.value) {
    searchSubject(searchNodeName.value)
  }
}

const menu = new G6.Menu({
  offsetX: 6,
  offsetY: 10,
  itemTypes: ['node'],
  getContent() {
    const outDiv = document.createElement('div');
    outDiv.style.width = '100px';
    outDiv.innerHTML = `<ul class='graph-context-menu'>
        <li name='parent' class='graph-context-menu-item'>上级学科</li>
        <li name='child' class='graph-context-menu-item'>下级学科</li>
        <li name='search' class='graph-context-menu-item'>搜索</li>
      </ul>`
    return outDiv
  },
  handleMenuClick(target: HTMLElement, item: Item) {
    const menuName = target.getAttribute("name")
    const model = item.getModel() as NodeConfig
    if (model.label) {
      if (menuName == 'parent') {
        if (model.nodeId) findParents(model.nodeId as number)
      } else if (menuName == 'child') {
        if (model.nodeId) findChildren(model.nodeId as number)
      } else if (menuName == 'search') {
        searchNodeName.value = model.label as string
        searchSubject(searchNodeName.value)
      }
    }
  },
});

function initGraph(width: number, height: number) {
  const graph = new G6.Graph({
    container: 'graph',
    width: width,
    height: height,
    layout: {
      type: 'force',
      preventOverlap: true,
      linkDistance: 150,
      nodeStrength: 150,
      nodeSize: 80,
    },
    modes: {
      // 允许拖拽画布、放缩画布、拖拽节点
      default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'click-select'],
    },
    nodeStateStyles: {
      selected: {
        stroke: '#A72184',
        fill: '#A72184',
      },
    },
    defaultNode: {
      size: 64,
      type: 'circle',
      style: {
        stroke: '#90EE90',
        fill: '#90EE90',
      },
      // 选中颜色为 #FBEECD
      labelCfg: {
        position: 'center',
        style: {
          fontSize: 11,
          fill: "#FFFFFF",
        },
      },
    },
    defaultEdge: {
      label: '下级学科',
      size: 1,
      color: '#B6BBC5',
      style: {
        endArrow: {
          path: 'M 0,0 L 8,4 L 8,-4 Z',
          fill: '#B6BBC5',
        },
      },
    },
    plugins: [menu],
  });
  // // 读取数据源到图上
  // graph.data(data as any);
  // // 渲染图
  // graph.render();
  // 监听节点点击事件
  // graph.on('node:click', event => {
  //   const nodes = data['nodes']
  //   const index = nodes.findIndex(n => n.id == event.item?.getID())
  //   if (index > -1) {
  //     const node = nodes[index];
  //     searchSubject(node.label);

  //     kgGraphStore.search(node.label);
  //   }
  // });
  return graph;
}

function onSearch() {
  graphDataManager.pruge();
  findName(keyword.value);
}

onMounted(() => {
  const rect = document.getElementById("graph")?.getBoundingClientRect()
  if (rect) {
    graph = initGraph(rect.width, rect.height)
    graph.node((node) => {
      if (node.label === "建筑工程" || node.label === "工程设计") {
        return {
          style: {
            fill: "#FDD900",
            stroke: "#FDD900",
          },
          labelCfg: {
            style: {
              fill: "#000000",
            }
          }
        };
      }
      return {
        style: {
          fill: "#0079C2",
          stroke: "#0079C2",
        },
      };
    });
    findName(keyword.value)
  }
});
</script>

<template>
  <div class="search-box">
    <span class="search-box-subjectName">学科名称</span>
    <a-input-search
      v-model:value="keyword"
      placeholder="请输入学科名称"
      enter-button
      size="large"
      @search="onSearch"
    />
  </div>
  <a-row :gutter="16" type="flex" class="graph-container">
    <a-col :span="18">
      <div class="graph-wrap">
        <div id="graph" class="graph"></div>
      </div>
    </a-col>
    <a-col :span="6">
      <div class="property-wrap">
        <PropertyComponent :data="propertyData" />
      </div>
    </a-col>
  </a-row>
  <div class="articles-wrap">
    <SpinComponent :spinning="searchLoading">
      <div v-if="searchResult">
        <SearchResultList :data="searchResult" class="articles" />
        <a-pagination
          v-model:current="currentPage"
          :total="searchResult.total"
          :show-total="(total: number) => `共 ${total} 条`"
          :show-size-changer="false"
          class="pagination"
          @change="onPaginationChange"
        />
      </div>
      <a-empty class="empty" v-else description="暂无数据，请点击学科导航节点" />
    </SpinComponent>
  </div>
</template>

<style scoped lang="scss">
@import "@/assets/variables.scss";

.search-box {
  width: 65%;
  margin: 0 auto;
  margin-top: 20px;
  display: flex;
  align-items: center;
  .search-box-subjectName {
    display: block;
    width: 6rem;
  }
}

.graph-container {
  margin-top: 20px;

  .graph-wrap,
  .property-wrap {
    border: 1px solid $border-color;
    background-color: white;
    padding: 10px;
  }

  .graph-wrap {
    height: 500px;

    .graph {
      width: 100%;
      height: 100%;
    }
  }

  .property-wrap {
    height: 100%;
  }

  :deep(ul.graph-context-menu) {
    list-style: none;
    margin: 0;
    padding: 0;

    li.graph-context-menu-item {
      font-size: 1.25em;
      padding: 5px 10px;
      margin-top: 6px;
      cursor: pointer;

      &:first-child {
        margin-top: 0;
      }

      &:hover {
        background-color: $bg-color-gray-light;
      }
    }
  }
}

.articles-wrap {
  margin-top: 20px;

  .articles {
    padding: 10px 20px;
    background-color: white;
    border: 1px solid $border-color;
  }

  .empty {
    background-color: white;
    border: 1px solid $border-color;
    margin: 0;
    padding: 30px 0;
  }

  .pagination {
    float: right;
    margin-top: 20px;

    ::after {
      clear: both;
    }
  }
}
</style>
