<template>
  <div class="pedigree-flow">
    <div class="flow-container" ref="containerRef"></div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { Graph } from '@antv/x6';

const { t, locale } = useI18n();

interface Props {
  currentNodeId?: string;
}

const props = withDefaults(defineProps<Props>(), {
  currentNodeId: 'inputModel',
});

const emits = defineEmits(['nodeClick']);

const containerRef = ref<HTMLElement>();
const graph = ref<Graph>();
const resizeObserver = ref<ResizeObserver>();

const nodeWidth = computed(() => (locale.value === 'EN' ? 160 : 100));
const nodeGap = computed(() => (locale.value === 'EN' ? 40 : 60));
const totalWidth = computed(() => 6 * nodeWidth.value + 5 * nodeGap.value);

const flowNodesConfig = [
  { id: 'inputModel', labelKey: '数据谱系.输入模型' },
  { id: 'simParams', labelKey: '数据谱系.仿真参数' },
  { id: 'calcModel', labelKey: '数据谱系.计算模型' },
  { id: 'calcResult', labelKey: '数据谱系.计算结果' },
  { id: 'keyResult', labelKey: '数据谱系.关键结果' },
  { id: 'simReport', labelKey: '数据谱系.仿真报告' },
];

const flowNodesData = computed(() =>
  flowNodesConfig.map((node) => ({
    id: node.id,
    label: t(node.labelKey),
  }))
);

const getStartXFun = (containerWidth: number) => {
  return containerWidth >= totalWidth.value ? (containerWidth - totalWidth.value) / 2 : 20;
};

const flowEdges = [
  { source: 'inputModel', target: 'simParams' },
  { source: 'simParams', target: 'calcModel' },
  { source: 'calcModel', target: 'calcResult' },
  { source: 'calcResult', target: 'keyResult' },
  { source: 'keyResult', target: 'simReport' },
];

const defaultBodyAttrs = {
  fill: '#fff',
  stroke: '#dcdfe6',
  strokeWidth: 1,
  rx: 4,
  ry: 4,
  cursor: 'pointer',
};

const defaultTextAttrs = {
  fill: '#606266',
  fontSize: 14,
  fontWeight: 'normal',
  cursor: 'pointer',
};

const selectedBodyAttrs = {
  fill: '#ecf5ff',
  stroke: '#409eff',
  strokeWidth: 2,
  rx: 4,
  ry: 4,
  cursor: 'pointer',
};

const selectedTextAttrs = {
  fill: '#409eff',
  fontSize: 14,
  fontWeight: 'bold',
  cursor: 'pointer',
};

const initGraphFun = () => {
  if (!containerRef.value) {
    return;
  }

  const containerWidth = containerRef.value.clientWidth || 1200;
  const startX = getStartXFun(containerWidth);

  graph.value = new Graph({
    container: containerRef.value,
    width: containerWidth,
    height: 160,
    autoResize: true,
    grid: true,
    panning: {
      enabled: true,
      modifiers: [],
    },
    interacting: {
      nodeMovable: false,
    },
    background: {
      color: '#F2F7FA',
    },
  });

  flowNodesData.value.forEach((nodeData, index) => {
    const isSelected = nodeData.id === props.currentNodeId;
    const x = startX + (nodeWidth.value + nodeGap.value) * index;

    graph.value?.addNode({
      id: nodeData.id,
      x: x,
      y: 60,
      width: nodeWidth.value,
      height: 40,
      label: nodeData.label,
      attrs: {
        body: isSelected ? selectedBodyAttrs : defaultBodyAttrs,
        label: isSelected ? selectedTextAttrs : defaultTextAttrs,
      },
    });
  });

  flowEdges.forEach((edge) => {
    graph.value?.addEdge({
      source: edge.source,
      target: edge.target,
      attrs: {
        line: {
          stroke: '#c0c4cc',
          strokeWidth: 1,
          targetMarker: {
            name: 'classic',
            size: 8,
          },
        },
      },
    });
  });

  graph.value.on('node:click', ({ node }) => {
    updateNodeStyleFun(node.id);
    emits('nodeClick', node.id);
  });

  graph.value.on('resize', ({ width }) => {
    updateNodesPositionFun(width);
  });
};

const updateNodesPositionFun = (containerWidth: number) => {
  if (!graph.value) {
    return;
  }

  const startX = getStartXFun(containerWidth);

  flowNodesData.value.forEach((nodeData, index) => {
    const node = graph.value?.getCellById(nodeData.id);
    if (node && node.isNode()) {
      const x = startX + (nodeWidth.value + nodeGap.value) * index;
      node.setPosition({ x, y: 60 });
    }
  });
};

const updateNodeStyleFun = (selectedId: string) => {
  if (!graph.value) {
    return;
  }

  flowNodesData.value.forEach((nodeData) => {
    const node = graph.value?.getCellById(nodeData.id);
    if (node && node.isNode()) {
      const isSelected = nodeData.id === selectedId;
      node.attr('body', isSelected ? selectedBodyAttrs : defaultBodyAttrs);
      node.attr('label/fill', isSelected ? selectedTextAttrs.fill : defaultTextAttrs.fill);
      node.attr(
        'label/fontWeight',
        isSelected ? selectedTextAttrs.fontWeight : defaultTextAttrs.fontWeight
      );
    }
  });
};

const setSelectedNodeFun = (nodeId: string) => {
  updateNodeStyleFun(nodeId);
};

const updateNodesLabelFun = () => {
  if (!graph.value) {
    return;
  }

  flowNodesConfig.forEach((nodeConfig) => {
    const node = graph.value?.getCellById(nodeConfig.id);
    if (node && node.isNode()) {
      node.attr('label/text', t(nodeConfig.labelKey));
    }
  });
};

const updateNodesSizeAndPositionFun = () => {
  if (!graph.value || !containerRef.value) {
    return;
  }

  const containerWidth = containerRef.value.clientWidth || 1200;
  const startX = getStartXFun(containerWidth);

  flowNodesConfig.forEach((nodeConfig, index) => {
    const node = graph.value?.getCellById(nodeConfig.id);
    if (node && node.isNode()) {
      const x = startX + (nodeWidth.value + nodeGap.value) * index;
      node.setPosition({ x, y: 60 });
      node.setSize({ width: nodeWidth.value, height: 40 });
    }
  });
};

watch(locale, () => {
  updateNodesLabelFun();
  updateNodesSizeAndPositionFun();
});

defineExpose({
  setSelectedNodeFun,
});

onMounted(() => {
  nextTick(() => {
    initGraphFun();
  });
});

onBeforeUnmount(() => {
  if (resizeObserver.value) {
    resizeObserver.value.disconnect();
  }
  graph.value?.dispose();
});
</script>

<style lang="scss" scoped>
.pedigree-flow {
  width: 100%;
  height: 160px;
  background: #fafafa;
  border-radius: 4px;
  overflow: hidden;

  .flow-container {
    width: 100%;
    height: 100%;
  }
}
</style>
