<!-- Sidebar: Switch data type -->
<template>
  <div
    class="flex flex-col justify-between py-4 w-14 h-screen-main absolute bottom-0 left-0 z-10"
    :class="sidebarLeftValue ? 'bg-neutral-50' : ''"
  >
    <ul class="space-y-4 flex flex-col justify-center items-center">
      <li
        class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary"
        @click="sidebarView = !sidebarView"
        :class="{ 'border-primary': sidebarView }"
        v-tooltip="tooltip.sidebarView"
      >
        <span
          class="material-symbols-outlined !text-2xl hover:text-primary p-1.5"
          :class="[sidebarView ? 'text-primary' : 'text-neutral-500']"
        >
          track_changes
        </span>
      </li>
      <li
        class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary"
        @click="sidebarFilter = !sidebarFilter"
        :class="{ 'border-primary': sidebarFilter }"
        v-tooltip="tooltip.sidebarFilter"
      >
        <span
          class="material-symbols-outlined !text-2xl hover:text-primary p-1.5"
          :class="[sidebarFilter ? 'text-primary' : 'text-neutral-500']"
          id="iconFilter"
        >
          tornado
        </span>
      </li>
      <li
        class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary"
        @click="sidebarTraces = !sidebarTraces"
        :class="{ 'border-primary': sidebarTraces }"
        v-tooltip="tooltip.sidebarTraces"
      >
        <span
          class="material-symbols-outlined !text-2xl hover:text-primary p-1.5"
          :class="[sidebarTraces ? 'text-primary' : 'text-neutral-500']"
        >
          rebase
        </span>
      </li>
    </ul>
  </div>

  <!-- Cytoscape Map -->
  <div class="min-w-full h-screen-main bg-neutral-50 -z-40">
    <div id="cy" class="min-w-full h-screen-main"></div>
  </div>

  <!-- Sidebar: State -->
  <div
    id="sidebar_state"
    class="bg-transparent py-4 w-14 h-screen-main z-10 bottom-0 right-0 absolute"
  >
    <ul class="flex flex-col justify-center items-center">
      <li
        class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary"
        @click="sidebarState = !sidebarState"
        :class="{ 'border-primary': sidebarState }"
        id="iconState"
        v-tooltip.left="tooltip.sidebarState"
      >
        <span
          class="material-symbols-outlined !text-2xl text-neutral-500 hover:text-primary p-1.5"
          :class="[sidebarState ? 'text-primary' : 'text-neutral-500']"
        >
          info
        </span>
      </li>
    </ul>
  </div>

  <!-- Sidebar Model -->
  <SidebarView
    v-model:visible="sidebarView"
    @switch-map-type="switchMapType"
    @switch-curve-styles="switchCurveStyles"
    @switch-rank="switchRank"
    @switch-data-layer-type="switchDataLayerType"
  ></SidebarView>
  <SidebarState
    v-model:visible="sidebarState"
    :insights="insights"
    :stats="stats"
  ></SidebarState>
  <SidebarTraces
    v-model:visible="sidebarTraces"
    :cases="cases"
    @switch-Trace-Id="switchTraceId"
    ref="tracesViewRef"
  >
  </SidebarTraces>
  <SidebarFilter
    v-model:visible="sidebarFilter"
    :filterTasks="filterTasks"
    :filterStartToEnd="filterStartToEnd"
    :filterEndToStart="filterEndToStart"
    :filterTimeframe="filterTimeframe"
    :filterTrace="filterTrace"
    @submit-all="createCy(mapType)"
    @switch-Trace-Id="switchTraceId"
    ref="sidebarFilterRefComp"
  ></SidebarFilter>
</template>

<script>
// The Lucia project.
// Copyright 2024-2026 DSP, inc.  All rights reserved.
// Authors:
//   cindy.chang@dsp.im (Cindy Chang), 2024/5/30
//   imacat.yang@dsp.im (imacat), 2023/9/23
/**
 * @module views/Compare/MapCompare
 *   Side-by-side process map comparison view with
 *   dual Cytoscape graphs.
 */

import { useConformanceStore as useConformanceStoreInGuard } from "@/stores/conformance";

export default {
  async beforeRouteEnter(to, from, next) {
    const isCheckPage = to.name.includes("Check");

    if (isCheckPage) {
      const conformanceStore = useConformanceStoreInGuard();
      switch (to.params.type) {
        case "log":
          conformanceStore.conformanceLogCreateCheckId = to.params.fileId;
          break;
        case "filter":
          conformanceStore.conformanceFilterCreateCheckId = to.params.fileId;
          break;
      }
      await conformanceStore.getConformanceReport(true);
      to.meta.file = conformanceStore.routeFile;
    }
    next();
  },
};
</script>

<script setup>
import { ref, computed, watch, onMounted, onBeforeMount, onBeforeUnmount } from "vue";
import { useRoute } from "vue-router";
import { storeToRefs } from "pinia";
import { useLoadingStore } from "@/stores/loading";
import { useAllMapDataStore } from "@/stores/allMapData";
import cytoscapeMap from "@/module/cytoscapeMap.js";
import { useCytoscapeStore } from "@/stores/cytoscapeStore";
import { useMapPathStore } from "@/stores/mapPathStore";
import emitter from "@/utils/emitter";
import SidebarView from "@/components/Discover/Map/SidebarView.vue";
import SidebarState from "@/components/Discover/Map/SidebarState.vue";
import SidebarTraces from "@/components/Discover/Map/SidebarTraces.vue";
import SidebarFilter from "@/components/Discover/Map/SidebarFilter.vue";
import ImgCapsule1 from "@/assets/capsule1.svg";
import ImgCapsule2 from "@/assets/capsule2.svg";
import ImgCapsule3 from "@/assets/capsule3.svg";
import ImgCapsule4 from "@/assets/capsule4.svg";

const ImgCapsules = [ImgCapsule1, ImgCapsule2, ImgCapsule3, ImgCapsule4];

const props = defineProps(["type", "checkType", "checkId", "checkFileId"]);

const route = useRoute();

// Stores
const loadingStore = useLoadingStore();
const allMapDataStore = useAllMapDataStore();
const { isLoading } = storeToRefs(loadingStore);
const {
  processMap,
  bpmn,
  stats,
  insights,
  traceId,
  traces,
  baseTraces,
  baseTraceId,
  filterTasks,
  filterStartToEnd,
  filterEndToStart,
  filterTimeframe,
  filterTrace,
  temporaryData,
  isRuleData,
  ruleData,
  logId,
  baseLogId,
  createFilterId,
  cases,
  postRuleData,
} = storeToRefs(allMapDataStore);

const cytoscapeStore = useCytoscapeStore();
const { setCurrentGraphId } = cytoscapeStore;
const mapPathStore = useMapPathStore();

const numberBeforeMapInRoute = computed(() => {
  const path = route.path;
  const segments = path.split("/");
  const mapIndex = segments.findIndex((segment) => segment.includes("map"));
  if (mapIndex > 0) {
    const previousSegment = segments[mapIndex - 1];
    const match = previousSegment.match(/\d+/);
    return match ? match[0] : "No number found";
  }
  return "No map segment found";
});

onBeforeMount(() => {
  setCurrentGraphId(numberBeforeMapInRoute.value);
});

// Data
const processMapData = ref({
  startId: 0,
  endId: 1,
  nodes: [],
  edges: [],
});
const bpmnData = ref({
  startId: 0,
  endId: 1,
  nodes: [],
  edges: [],
});
const cytoscapeGraph = ref(null);
const curveStyle = ref("unbundled-bezier");
const mapType = ref("processMap");
const dataLayerType = ref("freq");
const dataLayerOption = ref("total");
const rank = ref("LR");
const localTraceId = ref(1);
const sidebarView = ref(false);
const sidebarState = ref(false);
const sidebarTraces = ref(false);
const sidebarFilter = ref(false);
const infiniteFirstCases = ref(null);
const startNodeId = ref(-1);
const endNodeId = ref(-1);
const tracesViewRef = ref(null);
const sidebarFilterRefComp = ref(null);

const tooltip = {
  sidebarView: {
    value: "Visualization Setting",
    class: "ml-1",
    pt: {
      text: "text-[10px] p-1",
    },
  },
  sidebarTraces: {
    value: "Trace",
    class: "ml-1",
    pt: {
      text: "text-[10px] p-1",
    },
  },
  sidebarFilter: {
    value: "Filter",
    class: "ml-1",
    pt: {
      text: "text-[10px] p-1",
    },
  },
  sidebarState: {
    value: "Summary",
    class: "ml-1",
    pt: {
      text: "text-[10px] p-1",
    },
  },
};

// Computed
const sidebarLeftValue = computed(() => {
  return (
    sidebarView.value === true ||
    sidebarTraces.value === true ||
    sidebarFilter.value === true
  );
});

// Watch
watch(sidebarView, (newValue) => {
  if (newValue) {
    sidebarFilter.value = false;
    sidebarTraces.value = false;
  }
});

watch(sidebarFilter, (newValue) => {
  if (newValue) {
    sidebarView.value = false;
    sidebarState.value = false;
    sidebarTraces.value = false;
  }
});

watch(sidebarTraces, (newValue) => {
  if (newValue) {
    sidebarView.value = false;
    sidebarState.value = false;
    sidebarFilter.value = false;
  }
});

watch(sidebarState, (newValue) => {
  if (newValue) {
    sidebarFilter.value = false;
    sidebarTraces.value = false;
  }
});

// Methods
/**
 * Switches the map type and re-renders the Cytoscape graph.
 * @param {string} type - 'processMap' or 'bpmn'.
 */
function switchMapType(type) {
  mapType.value = type;
  createCy(type);
}

/**
 * Switches the edge curve style and re-renders the graph.
 * @param {string} style - The curve style.
 */
function switchCurveStyles(style) {
  curveStyle.value = style;
  createCy(mapType.value);
}

/**
 * Switches the layout direction and re-renders the graph.
 * @param {string} rankValue - 'LR' (horizontal) or 'TB' (vertical).
 */
function switchRank(rankValue) {
  rank.value = rankValue;
  createCy(mapType.value);
}

/**
 * Switches the data layer type/option and re-renders the graph.
 * @param {string} type - 'freq' or 'duration'.
 * @param {string} option - The data option (e.g., 'total', 'average').
 */
function switchDataLayerType(type, option) {
  dataLayerType.value = type;
  dataLayerOption.value = option;
  createCy(mapType.value);
}

/**
 * Switches the displayed trace and reloads its detail.
 * @param {object} e - Object containing the trace id.
 */
async function switchTraceId(e) {
  if (e.id === traceId.value) return;
  isLoading.value = true;
  traceId.value = e.id;
  await allMapDataStore.getTraceDetail();
  tracesViewRef.value.createCy();
  isLoading.value = false;
}

/**
 * Populates node data from the process map or BPMN model.
 * @param {object} mapData - The map data object to populate.
 */
function setNodesData(mapData) {
  const mapTypeVal = mapType.value;
  const logFreq = {
    total: "",
    rel_freq: "",
    average: "",
    median: "",
    max: "",
    min: "",
    cases: "",
  };
  const logDuration = {
    total: "",
    rel_duration: "",
    average: "",
    median: "",
    max: "",
    min: "",
  };
  const gateway = {
    parallel: "+",
    exclusive: "x",
    inclusive: "o",
  };

  mapData.nodes = [];
  const mapSource = mapTypeVal === "processMap" ? processMap.value : bpmn.value;
  mapSource.vertices.forEach((node) => {
    switch (node.type) {
      case "gateway":
        mapData.nodes.push({
          data: {
            id: node.id,
            type: node.type,
            label: gateway[node.gateway_type],
            height: 60,
            width: 60,
            backgroundColor: "#FFF",
            bordercolor: "#003366",
            shape: "diamond",
            freq: logFreq,
            duration: logDuration,
          },
        });
        break;
      case "event":
        if (node.event_type === "start") {
          mapData.startId = node.id;
          startNodeId.value = node.id;
        } else if (node.event_type === "end") {
          mapData.endId = node.id;
          endNodeId.value = node.id;
        }

        mapData.nodes.push({
          data: {
            id: node.id,
            type: node.type,
            label: node.event_type,
            height: 48,
            width: 48,
            backgroundColor: "#FFFFFF",
            bordercolor: "#0F172A",
            textColor: "#FF3366",
            shape: "ellipse",
            freq: logFreq,
            duration: logDuration,
          },
        });
        break;
      default:
        mapData.nodes.push({
          data: {
            id: node.id,
            type: node.type,
            label: node.label,
            height: 48,
            width: 216,
            textColor: "#0F172A",
            backgroundColor: "rgba(0, 0, 0, 0)",
            borderradius: 999,
            shape: "round-rectangle",
            freq: node.freq,
            duration: node.duration,
            backgroundOpacity: 0,
            borderOpacity: 0,
          },
        });
        break;
    }
  });
}

/**
 * Populates edge data from the process map or BPMN model.
 * @param {object} mapData - The map data object to populate.
 */
function setEdgesData(mapData) {
  const mapTypeVal = mapType.value;
  const logDuration = {
    total: "",
    rel_duration: "",
    average: "",
    median: "",
    max: "",
    min: "",
    cases: "",
  };

  mapData.edges = [];
  const mapSource = mapTypeVal === "processMap" ? processMap.value : bpmn.value;
  mapSource.edges.forEach((edge) => {
    mapData.edges.push({
      data: {
        source: edge.tail,
        target: edge.head,
        freq: edge.freq,
        duration: edge.duration === null ? logDuration : edge.duration,
        edgeStyle:
          edge.tail === startNodeId.value || edge.head === endNodeId.value
            ? "dotted"
            : "solid",
        lineWidth: 1,
      },
    });
  });
}

/**
 * Creates and renders the Cytoscape graph.
 * @param {string} type - 'processMap' or 'bpmn'.
 */
async function createCy(type) {
  const graphId = document.getElementById("cy");
  const mapData = type === "processMap" ? processMapData.value : bpmnData.value;
  const mapSource = type === "processMap" ? processMap.value : bpmn.value;

  if (mapSource.vertices.length !== 0) {
    setNodesData(mapData);
    setEdgesData(mapData);
    setActivityBgImage(mapData);
    cytoscapeGraph.value = await cytoscapeMap(
      mapData,
      dataLayerType.value,
      dataLayerOption.value,
      curveStyle.value,
      rank.value,
      graphId,
    );
    const processOrBPMN = mapType.value === "processMap" ? "process" : "bpmn";
    const curveType = curveStyle.value === "taxi" ? "elbow" : "curved";
    const directionType = rank.value === "LR" ? "horizontal" : "vertical";
    await mapPathStore.setCytoscape(
      cytoscapeGraph.value,
      processOrBPMN,
      curveType,
      directionType,
    );
  }
}

/**
 * Assigns background images to activity nodes based on data level grouping.
 * @param {object} mapData - The map data containing nodes.
 */
function setActivityBgImage(mapData) {
  const nodes = mapData.nodes;
  const groupSize = Math.floor(nodes.length / ImgCapsules.length);
  let nodeOptionArr = [];
  const leveledGroups = [];
  const activityNodeArray = nodes.filter(
    (node) => node.data.type === "activity",
  );
  activityNodeArray.forEach((node) =>
    nodeOptionArr.push(node.data[dataLayerType.value][dataLayerOption.value]),
  );
  nodeOptionArr = nodeOptionArr.sort((a, b) => a - b);
  for (let i = 0; i < ImgCapsules.length; i++) {
    const startIdx = i * groupSize;
    const endIdx =
      i === ImgCapsules.length - 1
        ? activityNodeArray.length
        : startIdx + groupSize;
    leveledGroups.push(nodeOptionArr.slice(startIdx, endIdx));
  }
  for (let level = 0; level < leveledGroups.length; level++) {
    leveledGroups[level].forEach((option) => {
      const curNodes = activityNodeArray.filter(
        (activityNode) =>
          activityNode.data[dataLayerType.value][dataLayerOption.value] ===
          option,
      );
      curNodes.forEach((curNode) => {
        curNode.data = {
          ...curNode.data,
          nodeImageUrl: ImgCapsules[level],
          level,
        };
      });
    });
  }
}

onMounted(async () => {
  try {
    const routeParams = route.params;
    const file = route.meta.file;
    const isCheckPage = route.name.includes("Check");

    isLoading.value = true;
    switch (routeParams.type) {
      case "log":
        if (isCheckPage) {
          logId.value = file.parent?.id;
          baseLogId.value = file.parent?.id;
        } else {
          logId.value = routeParams.fileId;
          baseLogId.value = routeParams.fileId;
        }
        break;
      case "filter":
        if (isCheckPage) {
          createFilterId.value = file.parent?.id;
        } else {
          createFilterId.value = routeParams.fileId;
        }
        await allMapDataStore.fetchFunnel(createFilterId.value);
        isRuleData.value = Array.from(temporaryData.value);
        ruleData.value = isRuleData.value.map((e) =>
          sidebarFilterRefComp.value.setRule(e),
        );
        break;
    }
    await allMapDataStore.getAllMapData();
    await allMapDataStore.getAllTrace();

    traceId.value = traces.value[0]?.id;
    baseTraceId.value = baseTraces.value[0]?.id;
    await createCy(mapType.value);
    await allMapDataStore.getFilterParams();
    await allMapDataStore.getTraceDetail();

    emitter.on("saveModal", (boolean) => {
      sidebarView.value = boolean;
      sidebarFilter.value = boolean;
      sidebarTraces.value = boolean;
      sidebarState.value = boolean;
    });
    emitter.on("leaveFilter", (boolean) => {
      sidebarView.value = boolean;
      sidebarFilter.value = boolean;
      sidebarTraces.value = boolean;
      sidebarState.value = boolean;
    });
  } catch (error) {
    console.error("Failed to initialize map compare:", error);
  } finally {
    isLoading.value = false;
  }
});

onBeforeUnmount(() => {
  emitter.off("saveModal");
  emitter.off("leaveFilter");
  cytoscapeGraph.value?.destroy();
  logId.value = null;
  createFilterId.value = null;
  temporaryData.value = [];
  postRuleData.value = [];
  ruleData.value = [];
});
</script>
