<template>
  <div class="node-chart-content" :style="{height: isFull ? '100vh': '100%'}">
    <div class="regend-box">
      <div class="listwrap">
        <span class="value">
          <el-progress :percentage="100" :color="'#00B54F'" :show-text="false" />
        </span>
        <span class="lable">错误率 <= 20%</span>
      </div>
      <div class="listwrap">
        <span class="value">
          <el-progress :percentage="100" :color="'#f0e495'" :show-text="false" />
        </span>
        <span class="lable">20% < 错误率 <= 40%</span>
      </div>
      <div class="listwrap">
        <span class="value">
          <el-progress :percentage="100" :color="'#F1A657'" :show-text="false" />
        </span>
        <span class="lable">40% < 错误率 <= 60%</span>
      </div>
      <div class="listwrap">
        <span class="value">
          <el-progress :percentage="100" :color="'#EA463D'" :show-text="false" />
        </span>
        <span class="lable">60% < 错误率 <= 80%</span>
      </div>
      <div class="listwrap">
        <span class="value">
          <el-progress :percentage="100" :color="'#8d0b05'" :show-text="false" />
        </span>
        <span class="lable">错误率 > 80%</span>
      </div>
    </div>
    <div :id="chartContentId" class="chart-box" />
    <el-drawer
      title="我是标题"
      :visible.sync="isShowSystem"
      :direction="'rtl'"
      append-to-body
      show-close
      size="80%"
      @close="drawerFireClose"
    >
      <SystemTab v-if="isShowSystem" :system-tab-type="systemTabType" />
    </el-drawer>
    <el-dialog
      title=""
      append-to-body
      :visible.sync="dialogVisible"
      width="60%"
      custom-class="system-dialog"
    >
      <!-- <el-tabs v-model="activeName" @tab-click="handleClick">
        <el-tab-pane v-for="tab in tabsComp" :key="tab.name" :name="tab.name">
          <span slot="label">
            <el-button size="small" round class="ob-header-btn">{{ tab.label }}</el-button>
          </span>
          {{ tab.label }}
        </el-tab-pane>
      </el-tabs> -->
      <div v-if="charDataLoading" v-loading="charDataLoading" class="system-topo-box" />
      <div v-else class="dialog-abnormalchart-box">
        <AbnormalChart :chart-data="charData" :is-table-in-drawer="false" :trace-kind="traceKind" :current-alias="currentAlias" :app-name="appName" :app-id="appId" :is-show-ying-btn="true" :chart-dom-id="'node-abnoraml'" />
      </div>
    </el-dialog>
  </div>
</template>
<script>
import EventBus from '@/utils/bus'
import SystemTab from '../../topology/systemTab.vue'
import G6 from '@antv/g6'
import AbnormalChart from './AbnormalChart.vue'
import { getAbnormalStatistics } from '@/api/mapping'
import storage from '@/utils/storage'
export default {
  components: {
    SystemTab,
    AbnormalChart
  },
  props: {
    chartContentId: {
      type: String,
      default: ''
    },
    graphData: {
      type: Object,
      default: () => {
        return {}
      }
    },
    isFull: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      appItem: {},
      isShowSystem: false,
      dialogVisible: false,
      activeName: '异常分析',
      charData: [],
      charDataLoading: false,
      defaultAppImg: require('@/assets/apply/appImg.svg'),
      isLoading: false,
      tabsComp: [{
        name: 'topoCon1',
        label: '黄金指标',
        component: 'TopoCon' // 组件名称
      }, {
        name: 'topoCon2',
        label: '异常分析',
        component: 'TopoCon' // 组件名称
      }],
      childQueryParams: {
        start_time: 0,
        end_time: 0
      },
      traceKind: 'app',
      currentAlias: '',
      appName: '',
      appId: '',
      systemTabType: 'topoCon'
    }
  },
  watch: {
    '$store.state.time.globalTimes': {
      handler(newValue, oldValue) {
        if (newValue) {
          this.childQueryParams.start_time = newValue.startTime
          this.childQueryParams.end_time = newValue.endTime
          // this.getAbnormalChartData() // 异常统计柱状图
        }
      },
      // immediate: true,
      deep: true
    }
  },
  created() {
    // 说明
    // appItem中的id和app_id是一样的
    this.appItem = storage.get('appsItem')
    const start_time = this.$store.state.time.globalTimes.startTime
    const end_time = this.$store.state.time.globalTimes.endTime
    this.childQueryParams.start_time = start_time
    this.childQueryParams.end_time = end_time
  },
  mounted() {
    setTimeout(() => {
      this.init(this.graphData)
    }, 1000)
    EventBus.$on('currentAppClick', (msg, node, activeName = 'topoCon') => {
      if (msg === 'root') {
        this.systemTabType = activeName
        this.isShowSystem = true
        this.dialogVisible = false
      } else {
        this.dialogVisible = true
        this.isShowSystem = false
        this.getAbnormalChartData(node.app_alias) // 异常统计柱状图
      }
    })
  },
  methods: {
    init(data) {
      /**
       * by Shiwu
       */
      if (data) {
        const nodeHeight = 60
        const nodeWidth = 120
        const fontColor = '#000'
        const fittingString = (str, maxWidth, fontSize) => {
          const ellipsis = '...'
          const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]
          let currentWidth = 0
          let res = str
          const pattern = new RegExp('[\u4E00-\u9FA5]+') // distinguish the Chinese charactors and letters
          str && str.split('').forEach((letter, i) => {
            if (currentWidth > maxWidth - ellipsisLength) return
            if (pattern.test(letter)) {
              // Chinese charactors
              currentWidth += fontSize
            } else {
              // get the width of single letter according to the fontSize
              currentWidth += G6.Util.getLetterWidth(letter, fontSize)
            }
            if (currentWidth > maxWidth - ellipsisLength) {
              res = `${str.substr(0, i)}${ellipsis}`
            }
          })
          return res
        }
        const extractString = (str, maxWidth, fontSize) => {
          let currentWidth = 0
          let res = str
          const pattern = new RegExp('[\u4E00-\u9FA5]+') // distinguish the Chinese charactors and letters
          str.split('').forEach((letter, i) => {
            if (currentWidth > maxWidth) return
            if (pattern.test(letter)) {
              // Chinese charactors
              currentWidth += fontSize
            } else {
              // get the width of single letter according to the fontSize
              currentWidth += G6.Util.getLetterWidth(letter, fontSize)
            }
            if (currentWidth > maxWidth) {
              res = `${str.substr(0, i)}\n${str.substr(i)}`
            }
          })
          return res
        }
        const setNodeFillCorlor = function(trace_error_rate) {
          if (trace_error_rate != null && trace_error_rate != undefined) {
            const errRate = trace_error_rate * 100
            if (errRate <= 20) {
              return '#00B54F'
            } else if (errRate > 20 && errRate <= 40) {
              return '#f0e495'
            } else if (errRate > 40 && errRate <= 60) {
              return '#F1A657'
            } else if (errRate > 60 && errRate <= 80) {
              return '#EA463D'
            } else if (errRate > 80) {
              return '#8d0b05'
            }
          }
          return '#00B54F'
        }
        G6.registerNode(
          'copy-node',
          {
            drawShape: function drawShape(cfg, group) {
              const rect = group.addShape('rect', {
                attrs: {
                  x: -60,
                  y: -30,
                  height: nodeHeight,
                  width: nodeWidth,
                  fill: setNodeFillCorlor(cfg.trace_error_rate),
                  stroke: setNodeFillCorlor(cfg.trace_error_rate),
                  lineWidth: 2,
                  radius: 8,
                  cursor: 'pointer'
                }
              })

              // 上部文字区域
              const topGroup = group.addGroup({
                // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
                name: 'top-group'
              })

              topGroup.addShape('image', {
                attrs: {
                  x: -10,
                  y: -20,
                  height: 20,
                  width: 20,
                  img: require('@/assets/apply/appImg.svg'),
                  opacity: 1,
                  cursor: 'pointer'
                },
                // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
                name: 'top-copy-img'
              })

              // 下部文字区域
              const bottomGroup = group.addGroup({
                // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
                name: 'bottom-group'
              })

              bottomGroup.addShape('text', {
                attrs: {
                  text: fittingString(cfg.app_name, nodeWidth - 10, 14),
                  x: 0,
                  y: 15,
                  fontSize: 12,
                  textAlign: 'center',
                  textBaseline: 'middle',
                  shadowColor: fontColor,
                  fill: fontColor,
                  cursor: 'pointer'
                },
                // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
                name: 'bottom-text'
              })
              return rect
            }
          },
          'single-node'
        )

        const container = document.getElementById(this.chartContentId)
        const width = container.scrollWidth
        const height = container.scrollHeight || 500
        const getPadding = function() {
          const screenWidth = window.screen.width
          const isOnlyOne = data.children.length === 0
          const isOnlyOne2 = data.downstream.length === 0 || data.upstream.length === 0

          let padding = [150]
          if (isOnlyOne) {
            if (screenWidth <= 1440) {
              padding = [100]
            }
            if (screenWidth === 1920) {
              padding = [230]
            }
          } else {
            padding = isOnlyOne2 ? [200] : [100]
            if (screenWidth <= 1440 && isOnlyOne2) {
              padding = [100]
            }
            if (screenWidth == 1680 && isOnlyOne2) {
              padding = [150]
            }
          }
          return padding
        }

        const graph = new G6.TreeGraph({
          container: this.chartContentId,
          width,
          height,
          fitView: true, // 自适应画布
          maxZoom: 8, // 默认值是10
          minZoom: 0.02, // 默认值是0.02
          fitCenter: true, // 是否平移图使其中心对齐到画布中心
          fitViewPadding: getPadding(),
          modes: {
            default: ['drag-canvas', 'drag-node', 'zoom-canvas',
              {
                type: 'tooltip', // 提示框
                formatText(model) {
                  if (model.side === 'root') {
                    return `
                    <div  style="background-color: rgba(0,0,0, 0.65);  box-shadow: rgb(174, 174, 174) 0px 0px 10px;
                      width: fit-content;
                      color: #fff;
                      padding: 10px;
                      border-radius:4px; ">
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">应用名称：</span>
                          <span>${model.app_name}</span>
                        </div>
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">链路总数：</span>
                          <span>${model.trace_num} 个</span>
                        </div>
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">平均延迟：</span>
                          <span>${model.trace_duration_average && Number(model.trace_duration_average).toFixed(2)} ms</span>
                        </div>
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">异常事件：</span>
                          <span>${model.exception_num || 0} 个</span>
                        </div>
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">错误数：</span>
                          <span>${model.trace_error_num} 个</span>
                        </div>
                        <div >
                          <span style="display:inline-block;width: 60px; text-align:right">错误率：</span>
                          <span>${model.trace_error_rate == 0 ? '0%' : model.trace_error_rate && (model.trace_error_rate * 100).toFixed(2) + '%'}</span>
                        </div>
                    </div>`
                  } else {
                    // 非当前应用提示框文本内容
                    return `
                    <div  style="background-color: rgba(0,0,0, 0.65);  box-shadow: rgb(174, 174, 174) 0px 0px 10px;
                      width: fit-content;
                      color: #fff;
                      padding: 10px;
                      border-radius:4px;">
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">链路总数：</span>
                          <span>${model.trace_num} 个</span>
                        </div>
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">平均延迟：</span>
                          <span>${model.trace_duration_average && Number(model.trace_duration_average).toFixed(2)} ms</span>
                        </div>
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">异常事件：</span>
                          <span>${model.exception_num || 0} 个</span>
                        </div>
                        <div style="margin-bottom:5px">
                          <span style="display:inline-block; width: 60px; text-align:right">错误数：</span>
                          <span>${model.trace_error_num} 个</span>
                        </div>
                        <div >
                          <span style="display:inline-block;width: 60px; text-align:right">错误率：</span>
                          <span>${model.trace_error_rate == 0 ? '0%' : model.trace_error_rate && (model.trace_error_rate * 100).toFixed(2) + '%'}</span>
                        </div>
                    </div>`
                  }
                }
              }
            ]
          },
          defaultNode: {
            /* node type */
            type: 'circle',
            /* node size */
            size: [40],

            style: {
              cursor: 'pointer'
            },
            labelCfg: {
              /* label's position, options: center, top, bottom, left, right */
              position: 'bottom'
            },
            /* icon configuration */
            icon: {
              /* whether show the icon, false by default */
              show: true,
              /* icon's img address, string type */
              img: this.defaultAppImg,
              /* icon's size */
              width: 14,
              height: 14
            },
            /* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
            nodeStateStyles: {
              // node style of active state
              active: {
                fillOpacity: 0.8,
                cursor: 'pointer'
              },
              // node style of selected state
              selected: {
                lineWidth: 5
              }
            }
          },
          defaultEdge: {
            // 使用自定义边
            // type: 'flow-line',
            type: 'cubic-horizontal',
            labelCfg: {
              cursor: 'pointer',
              autoRotate: true,
              style: {
                fontSize: 5,
                fill: '#999'
              }
            }

          },
          layout: {
            type: 'mindmap',
            direction: 'H',
            // linkDistance: 500,
            // nodeSpacing: 500,
            preventOverlap: true,
            getHeight: () => {
              return 36
            },
            getWidth: () => {
              return 36
            },
            getVGap: () => {
              return 30
            },
            getHGap: () => {
              return data.children.length > 50 ? 400 : (data.children.length > 20 ? 300 : 80)
            },
            getSide: (d) => {
              if (d.data.side === 'left') {
                return 'left'
              } else if (d.data.side === 'right') {
                return 'right'
              }
            }
          }
        })
        graph.node(function(node) {
          return {
            label: node.side === 'root' ? '' : node.app_name || node.app_alias,
            type: node.side === 'root' ? 'copy-node' : 'circle',
            style: {
              fill: setNodeFillCorlor(node.trace_error_rate),
              stroke: setNodeFillCorlor(node.trace_error_rate)
            }
          }
        })
        graph.edge(function(edge) {
          const isUp = edge.target.indexOf('left') !== -1
          const target = edge.target
          let label = ''
          if (data.children && data.children.length > 0) {
            data.children.forEach((item) => {
              if (target == item.id) {
                const disErrRate = item.error_rate === 0 ? '0%' : (item.error_rate * 100).toFixed(2) + '%'
                const disDuration = item.duration_average && item.duration_average.toFixed(2)
                label = extractString(`调用：${item.request_total}次  平均延迟：${disDuration}毫秒 错误率：${disErrRate}`, 80, 12)
              }
            })
          }
          return {
            label: label,
            style: {
              startArrow: isUp,
              endArrow: !isUp
            }
          }
        })

        graph.data(data)
        graph.render()
        graph.fitView()

        if (typeof window !== 'undefined') {
          window.onresize = () => {
            if (!graph || graph.get('destroyed')) return
            if (!container || !container.scrollWidth || !container.scrollHeight) return
            graph.changeSize(container.scrollWidth, container.scrollHeight)
          }
        }

        graph.on('node:click', (evt) => {
          const { item } = evt
          const node = item._cfg.model
          this.currentAlias = node.app_alias
          this.charData = []
          this.dialogVisible = false
          if (node.side === 'root') {
            this.systemTabType = 'topoCon'
            this.isShowSystem = true
            this.dialogVisible = false
          } else {
            this.appName = node.app_name
            this.appId = node.id
            this.dialogVisible = true
            this.isShowSystem = false
            this.getAbnormalChartData(node.app_alias) // 异常统计柱状图
          }
        })
        graph.on('edge:mouseenter', (evt) => {
          const { item } = evt
          graph.setItemState(item, 'active', true)
        })

        graph.on('edge:mouseleave', (evt) => {
          const { item } = evt
          graph.setItemState(item, 'active', false)
        })
      }
    },

    async getAbnormalChartData(app_alias) { // 获取异常统计数据
      this.charData = []
      this.charDataLoading = true
      const end_time = this.childQueryParams.end_time
      const start_time = this.childQueryParams.start_time
      const params = {
        kind: 'app',
        subcondition_value: app_alias,
        start_time: start_time,
        end_time: end_time,
        app_alias: app_alias
      }

      const res = await getAbnormalStatistics(params)
      if (res && res.code == 200) {
        this.charData = res.data
      } else {
        this.charData = []
      }
      this.charDataLoading = false
    },
    drawerFireClose() {
      // this.isShowAllBusiness = false
    },
    handleClick(val) {}
  }

}
</script>
<style lang="scss" scoped>
.node-chart-content{
  width:100%;
  height: 100%;
  position: relative;
}
.system-topo-box{
  height: 250px;
  width: 100%;
}
.dialog-abnormalchart-box{
  height: 300px;
  width: 100%;
}
::v-deep .el-tabs__nav-wrap::after{
    height: 0;
  }
::v-deep .el-tabs__item.is-active .el-button{
  background: #F2F3F5;
  font-weight: 500;
  font-size: 14px;
  color: #165DFF;
  border:none;
}
::v-deep .el-tabs__active-bar{
  height:0;
}
::v-deep .el-tabs__item{
  padding: 0 10px;
}
::v-deep .el-dialog__header{
  padding: 0;
}
.ob-header-btn{
  width: 110px;
  box-sizing: border-box;
  font-size: 14px;
}
.chart-box{
  height: 100%;
  width: 100%;
}
::v-deep .system-dialog{
  height: 800px;
}
.regend-box{
  display: flex;
  padding-left:10px;
  margin-top:5px;
}
.listwrap{
  color:#666;
  margin-right: 15px;
  span{
    display: inline-block;
    padding:4px;
  }
  .value{
    width:50px;
    box-sizing: border-box;
  }
}
</style>
