<!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
  <div class="link-settings">
    <h5 class="title">{{ t("callSettings") }}</h5>
    <div class="label">{{ t("linkDashboard") }}</div>
    <Selector
      :value="states.linkDashboard"
      :options="states.linkDashboards"
      size="small"
      placeholder="Please input a dashboard name for calls"
      @change="changeLinkDashboard"
      class="inputs"
      :clearable="true"
    />
    <div class="label">
      <span>{{ t("linkServerMetrics") }}</span>
      <el-popover
        placement="left"
        :width="400"
        trigger="click"
        effect="dark"
        v-if="states.linkServerMetrics.length"
      >
        <template #reference>
          <span @click="setConfigType('linkServerMetricConfig')">
            <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
          </span>
        </template>
        <Metrics
          :type="configType"
          :metrics="states.linkServerMetrics"
          @update="changeLinkServerMetrics"
        />
      </el-popover>
    </div>
    <Selector
      class="inputs"
      :multiple="true"
      :value="states.linkServerMetrics"
      :options="states.linkMetricList"
      size="small"
      placeholder="Select metrics"
      @change="updateLinkServerMetrics"
    />
    <span v-show="dashboardStore.entity !== EntityType[2].value">
      <div class="label">
        <span>{{ t("linkClientMetrics") }}</span>
        <el-popover
          placement="left"
          :width="400"
          trigger="click"
          effect="dark"
          v-if="states.linkClientMetrics.length"
        >
          <template #reference>
            <span @click="setConfigType('linkClientMetricConfig')">
              <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
            </span>
          </template>
          <Metrics
            :type="configType"
            :metrics="states.linkClientMetrics"
            @update="changeLinkClientMetrics"
          />
        </el-popover>
      </div>
      <Selector
        class="inputs"
        :multiple="true"
        :value="states.linkClientMetrics"
        :options="states.linkMetricList"
        size="small"
        placeholder="Select metrics"
        @change="updateLinkClientMetrics"
      />
    </span>
  </div>
  <div class="node-settings">
    <h5 class="title">{{ t("nodeSettings") }}</h5>
    <div class="label">{{ t("nodeDashboard") }}</div>
    <Selector
      v-show="!isService"
      :value="states.nodeDashboard"
      :options="states.nodeDashboards"
      size="small"
      placeholder="Please input a dashboard name for nodes"
      @change="changeNodeDashboard"
      class="inputs"
    />
    <div
      v-show="isService"
      v-for="(item, index) in items"
      :key="index"
      class="metric-item"
    >
      <Selector
        :value="item.scope"
        :options="ScopeType"
        size="small"
        placeholder="Select a scope"
        @change="changeScope(index, $event)"
        class="item mr-5"
      />
      <Selector
        :value="item.dashboard"
        :options="states.nodeDashboards"
        size="small"
        placeholder="Please input a dashboard name for nodes"
        @change="updateNodeDashboards(index, $event)"
        class="item mr-5"
      />
      <span>
        <Icon
          class="cp mr-5"
          iconName="remove_circle_outline"
          size="middle"
          @click="deleteItem(index)"
        />
        <Icon
          class="cp"
          v-show="index === items.length - 1 && items.length < 5"
          iconName="add_circle_outlinecontrol_point"
          size="middle"
          @click="addItem"
        />
      </span>
    </div>
    <div class="label">
      <span>{{ t("nodeMetrics") }}</span>
      <el-popover
        placement="left"
        :width="400"
        trigger="click"
        effect="dark"
        v-if="states.nodeMetrics.length"
      >
        <template #reference>
          <span @click="setConfigType('nodeMetricConfig')">
            <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
          </span>
        </template>
        <Metrics
          :type="configType"
          :metrics="states.nodeMetrics"
          @update="changeNodeMetrics"
        />
      </el-popover>
    </div>
    <Selector
      class="inputs"
      :multiple="true"
      :value="states.nodeMetrics"
      :options="states.nodeMetricList"
      size="small"
      placeholder="Select metrics"
      @change="updateNodeMetrics"
    />
  </div>
  <div class="legend-settings" v-show="isService">
    <h5 class="title">{{ t("legendSettings") }}</h5>
    <div class="label">{{ t("conditions") }}</div>
    <div v-for="(metric, index) of legend.metric" :key="metric.name + index">
      <Selector
        class="item"
        :value="metric.name"
        :options="states.nodeMetricList"
        size="small"
        placeholder="Select a metric"
        @change="changeLegend(LegendOpt.NAME, $event, index)"
      />
      <Selector
        class="input-small"
        :value="metric.condition"
        :options="MetricConditions"
        size="small"
        placeholder="Select a condition"
        @change="changeLegend(LegendOpt.CONDITION, $event, index)"
      />
      <el-input
        v-model="metric.value"
        placeholder="Please input a value"
        type="number"
        @change="changeLegend(LegendOpt.VALUE, $event, index)"
        size="small"
        class="item"
      />
      <span>
        <Icon
          class="cp delete"
          iconName="remove_circle_outline"
          size="middle"
          @click="deleteMetric(index)"
        />
        <Icon
          class="cp"
          iconName="add_circle_outlinecontrol_point"
          size="middle"
          v-show="
            index === legend.metric.length - 1 && legend.metric.length < 5
          "
          @click="addMetric"
        />
      </span>
      <div v-show="index !== legend.metric.length - 1">&&</div>
    </div>
    <div class="label">Healthy Description</div>
    <el-input
      v-model="description.healthy"
      placeholder="Please input description"
      size="small"
      class="mt-5"
    />
    <div class="label">Unhealthy Description</div>
    <el-input
      v-model="description.unhealthy"
      placeholder="Please input description"
      size="small"
      class="mt-5"
    />
    <el-button
      @click="setLegend"
      class="legend-btn"
      size="small"
      type="primary"
    >
      {{ t("setLegend") }}
    </el-button>
  </div>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue";
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useTopologyStore } from "@/store/modules/topology";
import { ElMessage } from "element-plus";
import { MetricCatalog, ScopeType, MetricConditions } from "../../../data";
import { Option } from "@/types/app";
import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
import { Node } from "@/types/topology";
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
import { EntityType, LegendOpt, MetricsType } from "../../../data";
import Metrics from "./Metrics.vue";

/*global defineEmits */
const emit = defineEmits(["update", "updateNodes"]);
const { t } = useI18n();
const dashboardStore = useDashboardStore();
const topologyStore = useTopologyStore();
const { selectedGrid } = dashboardStore;
const nodeDashboard =
  selectedGrid.nodeDashboard && selectedGrid.nodeDashboard.length
    ? selectedGrid.nodeDashboard
    : "";
const isService = [EntityType[0].value, EntityType[1].value].includes(
  dashboardStore.entity
);
const items = reactive<
  {
    scope: string;
    dashboard: string;
  }[]
>(isService && nodeDashboard ? nodeDashboard : [{ scope: "", dashboard: "" }]);
const states = reactive<{
  linkDashboard: string;
  nodeDashboard: {
    scope: string;
    dashboard: string;
  }[];
  linkServerMetrics: string[];
  linkClientMetrics: string[];
  nodeMetrics: string[];
  nodeMetricList: Option[];
  linkMetricList: Option[];
  linkDashboards: (DashboardItem & { label: string; value: string })[];
  nodeDashboards: (DashboardItem & { label: string; value: string })[];
}>({
  linkDashboard: selectedGrid.linkDashboard || "",
  nodeDashboard: selectedGrid.nodeDashboard || [],
  linkServerMetrics: selectedGrid.linkServerMetrics || [],
  linkClientMetrics: selectedGrid.linkClientMetrics || [],
  nodeMetrics: selectedGrid.nodeMetrics || [],
  nodeMetricList: [],
  linkMetricList: [],
  linkDashboards: [],
  nodeDashboards: [],
});
const l = selectedGrid.legend && selectedGrid.legend.length;
const legend = reactive<{
  metric: { name: string; condition: string; value: string }[];
}>({
  metric: l ? selectedGrid.legend : [{ name: "", condition: "", value: "" }],
});
const configType = ref<string>("");
const description = reactive<any>(selectedGrid.description || {});

getMetricList();
async function getMetricList() {
  const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
  const json = await dashboardStore.fetchMetricList();
  if (json.errors) {
    ElMessage.error(json.errors);
    return;
  }
  const entity =
    dashboardStore.entity === EntityType[1].value
      ? EntityType[0].value
      : dashboardStore.entity === EntityType[4].value
      ? EntityType[3].value
      : dashboardStore.entity;
  states.linkDashboards = list.reduce(
    (
      prev: (DashboardItem & { label: string; value: string })[],
      d: DashboardItem
    ) => {
      if (
        d.layer === dashboardStore.layerId &&
        d.entity === entity + "Relation"
      ) {
        prev.push({ ...d, label: d.name, value: d.name });
      }
      return prev;
    },
    []
  );
  states.nodeMetricList = (json.data.metrics || []).filter(
    (d: { type: string }) => d.type === MetricsType.REGULAR_VALUE
  );
  states.linkMetricList = (json.data.metrics || []).filter(
    (d: { catalog: string; type: string }) =>
      entity + "Relation" === (MetricCatalog as any)[d.catalog] &&
      d.type === MetricsType.REGULAR_VALUE
  );
  if (isService) {
    return;
  }
  states.nodeDashboards = list.reduce(
    (
      prev: (DashboardItem & { label: string; value: string })[],
      d: DashboardItem
    ) => {
      if (d.layer === dashboardStore.layerId && d.entity === entity) {
        prev.push({ ...d, label: d.name, value: d.name });
      }
      return prev;
    },
    []
  );
}
async function setLegend() {
  updateSettings();
  const ids = topologyStore.nodes.map((d: Node) => d.id);
  const names = dashboardStore.selectedGrid.legend.map((d: any) => d.name);
  if (!names.length) {
    emit("updateNodes");
    return;
  }
  const param = await useQueryTopologyMetrics(names, ids);
  const res = await topologyStore.getLegendMetrics(param);

  if (res.errors) {
    ElMessage.error(res.errors);
  }
  emit("updateNodes");
}
function changeNodeDashboard(opt: any) {
  states.nodeDashboard = opt[0].value;
  updateSettings();
}
function changeLinkDashboard(opt: any) {
  states.linkDashboard = opt[0].value;
  updateSettings();
}
function changeLegend(type: string, opt: any, index: number) {
  (legend.metric[index] as any)[type] = opt[0].value || opt;
}
function changeScope(index: number, opt: Option[] | any) {
  items[index].scope = opt[0].value;
  const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
  states.nodeDashboards = list.reduce(
    (
      prev: (DashboardItem & { label: string; value: string })[],
      d: DashboardItem
    ) => {
      if (d.layer === dashboardStore.layerId && d.entity === opt[0].value) {
        prev.push({ ...d, label: d.name, value: d.name });
      }
      return prev;
    },
    []
  );
  items[index].dashboard = states.nodeDashboards[0].value;
  updateSettings();
}
function updateNodeDashboards(index: number, content: Option[] | any) {
  items[index].dashboard = content[0].value;
  updateSettings();
}
function addItem() {
  items.push({ scope: "", dashboard: "" });
}
function deleteItem(index: number) {
  if (items.length === 1) {
    items.push({ scope: "", dashboard: "" });
  }
  items.splice(index, 1);
  updateSettings();
}
function updateSettings(metricConfig?: { [key: string]: MetricConfigOpt[] }) {
  const metrics = legend.metric.filter(
    (d: any) => d.name && d.value && d.condition
  );
  const param = {
    ...dashboardStore.selectedGrid,
    linkDashboard: states.linkDashboard,
    nodeDashboard: isService
      ? items.filter((d: { scope: string; dashboard: string }) => d.dashboard)
      : states.nodeDashboard,
    linkServerMetrics: states.linkServerMetrics,
    linkClientMetrics: states.linkClientMetrics,
    nodeMetrics: states.nodeMetrics,
    legend: metrics,
    ...metricConfig,
    description,
  };
  dashboardStore.selectWidget(param);
  dashboardStore.setConfigs(param);
  emit("update", param);
}
function updateLinkServerMetrics(options: Option[] | any) {
  const opt = options.map((d: Option) => d.value);
  const index = states.linkServerMetrics.findIndex(
    (d: any) => !opt.includes(d)
  );
  states.linkServerMetrics = opt;
  if (index < 0) {
    changeLinkServerMetrics();
    return;
  }
  const origin = dashboardStore.selectedGrid.linkServerMetricConfig || [];
  const config = origin.length === 1 ? [] : origin.splice(index, 1);
  changeLinkServerMetrics({ linkServerMetricConfig: config });
}
async function changeLinkServerMetrics(config?: {
  [key: string]: MetricConfigOpt[];
}) {
  updateSettings(config);
  if (!states.linkServerMetrics.length) {
    topologyStore.setLinkServerMetrics({});
    return;
  }
  topologyStore.getLinkServerMetrics(states.linkServerMetrics);
}
function updateLinkClientMetrics(options: Option[] | any) {
  const opt = options.map((d: Option) => d.value);
  const index = states.linkClientMetrics.findIndex(
    (d: any) => !opt.includes(d)
  );
  states.linkClientMetrics = opt;
  if (index < 0) {
    changeLinkClientMetrics();
    return;
  }
  const origin = dashboardStore.selectedGrid.linkClientMetricConfig || [];
  const config = origin.length === 1 ? [] : origin.splice(index, 1);
  changeLinkClientMetrics({ linkClientMetricConfig: config });
}
async function changeLinkClientMetrics(config?: {
  [key: string]: MetricConfigOpt[];
}) {
  updateSettings(config);
  if (!states.linkClientMetrics.length) {
    topologyStore.setLinkClientMetrics({});
    return;
  }
  topologyStore.getLinkClientMetrics(states.linkClientMetrics);
}
function updateNodeMetrics(options: Option[] | any) {
  const opt = options.map((d: Option) => d.value);
  const index = states.nodeMetrics.findIndex((d: any) => !opt.includes(d));
  states.nodeMetrics = opt;
  if (index < 0) {
    changeNodeMetrics();
    return;
  }
  const origin = dashboardStore.selectedGrid.nodeMetricConfig || [];
  const config = origin.length === 1 ? [] : origin.splice(index, 1);
  changeNodeMetrics({ nodeMetricConfig: config });
}
async function changeNodeMetrics(config?: {
  [key: string]: MetricConfigOpt[];
}) {
  updateSettings(config);
  if (!states.nodeMetrics.length) {
    topologyStore.setNodeMetricValue({});
    return;
  }
  topologyStore.queryNodeMetrics(states.nodeMetrics);
}
function deleteMetric(index: number) {
  if (legend.metric.length === 1) {
    legend.metric = [{ name: "", condition: "", value: "" }];
    return;
  }
  legend.metric.splice(index, 1);
}
function addMetric() {
  legend.metric.push({ name: "", condition: "", value: "" });
}
function setConfigType(type: string) {
  configType.value = type;
}
</script>
<style lang="scss" scoped>
.link-settings {
  margin-bottom: 20px;
}

.inputs {
  margin-top: 8px;
  width: 355px;
}

.item {
  width: 130px;
  margin-top: 5px;
}

.input-small {
  width: 45px;
  margin: 0 3px;
}

.title {
  margin-bottom: 0;
}

.label {
  font-size: 12px;
  margin-top: 10px;
}

.legend-btn {
  margin: 20px 0;
  cursor: pointer;
}

.delete {
  margin: 0 3px;
}
</style>
