<template>
  <Drawer
    :visible="sidebarFilter"
    :closeIcon="'pi pi-chevron-left'"
    :modal="false"
    position="left"
    :dismissable="false"
    :autoZIndex="false"
    class="!w-11/12 !bg-neutral-100"
  >
    <template #header>
      <ul class="flex space-x-4">
        <li
          class="h1 border-r-2 border-neutral-300 pr-4 cursor-pointer hover:text-neutral-900 hover:duration-700"
          @click="switchTab('filter')"
          :class="tab === 'filter' ? 'text-neutral-900' : 'text-neutral-500'"
          id="tabFilter"
        >
          Filter
        </li>
        <li
          class="h1 border-r-2 border-neutral-300 pr-4 cursor-pointer hover:text-neutral-900 hover:duration-700"
          @click="switchTab('funnel')"
          :class="tab === 'funnel' ? 'text-neutral-900' : 'text-neutral-500'"
          id="tabFunnel"
        >
          Funnel
        </li>
      </ul>
    </template>
    <!-- header: filter -->
    <div
      v-if="tab === 'filter'"
      class="pt-4 flex w-full h-full text-neutral-900"
    >
      <!-- title: filter silect -->
      <div class="space-y-2 mr-4 w-48 text-sm">
        <div>
          <p class="h2">Filter Type</p>
          <div
            v-for="(item, index) in selectFilter['Filter Type']"
            :key="index"
          >
            <RadioButton
              v-model="selectValue[0]"
              :inputId="item + index"
              name="Filter Type"
              :value="item"
              class="mb-1 mr-2"
              @change="radioFilterType"
            />
            <label :for="item + index">{{ item }}</label>
          </div>
        </div>
        <div v-show="selectValue[0] === 'Sequence'">
          <p class="h2">Activity Sequence</p>
          <div
            v-for="(item, index) in selectFilter['Activity Sequence']"
            :key="index"
          >
            <RadioButton
              v-model="selectValue[1]"
              :inputId="item + index"
              name="Activity Sequence"
              :value="item"
              class="mb-1 mr-2"
              @change="radioActSeq"
            />
            <label :for="item + index">{{ item }}</label>
          </div>
        </div>
        <div
          v-show="
            selectValue[0] === 'Sequence' && selectValue[1] === 'Start & End'
          "
        >
          <p class="h2">Start & End</p>
          <div
            v-for="(item, index) in selectFilter['Start & End']"
            :key="index"
          >
            <RadioButton
              v-model="selectValue[2]"
              :inputId="item + index"
              name="Start & End"
              :value="item"
              class="mb-1 mr-2"
              @change="radioStartAndEnd"
            />
            <label :for="item + index">{{ item }}</label>
          </div>
        </div>
        <div
          v-show="
            selectValue[0] === 'Sequence' && selectValue[1] === 'Sequence'
          "
        >
          <p class="h2">Mode</p>
          <div v-for="(item, index) in selectFilter['Mode']" :key="index">
            <RadioButton
              v-model="selectValue[3]"
              :inputId="item + index"
              name="Mode"
              :value="item"
              class="mb-1 mr-2"
            />
            <label :for="item + index">{{ item }}</label>
          </div>
        </div>
        <div>
          <p class="h2">Refine</p>
          <div v-for="(item, index) in selectFilter['Refine']" :key="index">
            <RadioButton
              v-model="selectValue[5]"
              :inputId="item + index"
              name="Refine"
              :value="item"
              class="mb-1 mr-2"
            />
            <label :for="item + index">{{ item }}</label>
          </div>
        </div>
        <div v-show="selectValue[0] === 'Timeframes'">
          <p class="h2">
            Containment<span
              class="material-symbols-outlined !text-sm align-middle ml-2 cursor-pointer"
              v-tooltip.bottom="tooltip.containment"
              >info</span
            >
          </p>
          <div
            v-for="(item, index) in selectFilter['Containment']"
            :key="index"
          >
            <RadioButton
              v-model="selectValue[6]"
              :inputId="item + index"
              name="Containment"
              :value="item"
              class="mb-1 mr-2"
            />
            <label :for="item + index">{{ item }}</label>
          </div>
        </div>
      </div>
      <!-- Fiter content -->
      <div class="w-[calc(100%_-_208px)] h-full">
        <!-- Filter titles -->
        <div class="w-full h-[calc(100%_-_56px)] mb-2">
          <!-- title: Activity Select  -->
          <div
            class="w-full h-[calc(100%_-_48px)]"
            v-if="selectValue[0] === 'Sequence'"
          >
            <p class="h2 ml-1 mb-2">Activity Select</p>
            <!-- Filter task Data-->
            <ActOccCase
              v-if="
                selectValue[0] === 'Sequence' &&
                selectValue[1] === 'Have activity(s)'
              "
              :tableTitle="'Activity List'"
              :tableData="filterAllTaskData"
              :tableSelect="selectFilterTask"
              :progressWidth="progressWidth"
              @on-row-select="onRowAct"
            ></ActOccCase>
            <!-- Filter Start Data -->
            <ActOcc
              v-if="
                selectValue[0] === 'Sequence' &&
                selectValue[1] === 'Start & End' &&
                selectValue[2] === 'Start'
              "
              :tableTitle="'Start activity'"
              :tableData="filterStartData"
              :tableSelect="selectFilterStart"
              :progressWidth="progressWidth"
              @on-row-select="onRowStart"
            ></ActOcc>
            <!-- Filter End Data -->
            <ActOcc
              v-if="
                selectValue[0] === 'Sequence' &&
                selectValue[1] === 'Start & End' &&
                selectValue[2] === 'End'
              "
              :tableTitle="'End activity'"
              :tableData="filterEndData"
              :tableSelect="selectFilterEnd"
              :progressWidth="progressWidth"
              @on-row-select="onRowEnd"
            ></ActOcc>
            <!-- Filter Start And End Data -->
            <div
              v-if="
                selectValue[0] === 'Sequence' &&
                selectValue[1] === 'Start & End' &&
                selectValue[2] === 'Start & End'
              "
              class="flex justify-between items-center w-full h-full space-x-4"
            >
              <ActOcc
                :tableTitle="'Start activity'"
                :tableData="filterStartToEndData"
                :tableSelect="selectFilterStartToEnd"
                :progressWidth="progressWidth"
                class="w-1/2"
                @on-row-select="startRow"
              ></ActOcc>
              <ActOcc
                :tableTitle="'End activity'"
                :tableData="filterEndToStartData"
                :tableSelect="selectFilterEndToStart"
                :progressWidth="progressWidth"
                class="w-1/2"
                @on-row-select="endRow"
              ></ActOcc>
            </div>
            <!-- Filter Sequence -->
            <div
              v-if="
                selectValue[0] === 'Sequence' && selectValue[1] === 'Sequence'
              "
              class="flex justify-between items-center w-full h-full space-x-4"
            >
              <ActAndSeq
                :filterTaskData="filterTaskData"
                :progressWidth="progressWidth"
                :listSeq="listSeq"
                @update:listSeq="onUpdateListSeq"
              ></ActAndSeq>
            </div>
          </div>
          <!-- title: Attributes -->
          <Attributes
            v-if="selectValue[0] === 'Attributes'"
            @select-attribute="getSelectAttribute"
          ></Attributes>
          <!-- title: Trace -->
          <Trace
            v-if="selectValue[0] === 'Trace'"
            ref="filterTraceViewRef"
            @filter-trace-selectArea="selectTraceArea = $event"
          ></Trace>
          <!-- title: Timeframes -->
          <Timeframes
            v-if="selectValue[0] === 'Timeframes'"
            :selectValue="selectValue"
          ></Timeframes>
        </div>

        <!-- Button -->
        <div class="float-right space-x-4 px-4 py-2">
          <button type="button" class="btn btn-sm btn-neutral" @click="reset">
            Clear
          </button>
          <button
            type="button"
            class="btn btn-sm"
            @click="submit"
            :disabled="isDisabledButton"
            :class="isDisabledButton ? 'btn-disable' : 'btn-neutral'"
          >
            Apply
          </button>
        </div>
      </div>
    </div>
    <!-- header: funnel -->
    <Funnel v-if="tab === 'funnel'" @submit-all="sumbitAll"></Funnel>
  </Drawer>
</template>

<script setup>
// The Lucia project.
// Copyright 2023-2026 DSP, inc.  All rights reserved.
// Authors:
//   chiayin.kuo@dsp.im (chiayin), 2023/1/31
/**
 * @module components/Discover/Map/SidebarFilter
 *   Filter sidebar for the Map view with tabbed panels
 *   for activity, timeframe, attribute, and trace
 *   filters.
 */

import { ref, reactive, computed } from "vue";
import { storeToRefs } from "pinia";
import { useToast } from "vue-toast-notification";
import { useLoadingStore } from "@/stores/loading";
import { useAllMapDataStore } from "@/stores/allMapData";
import ActOccCase from "@/components/Discover/Map/Filter/ActOccCase.vue";
import ActOcc from "@/components/Discover/Map/Filter/ActOcc.vue";
import ActAndSeq from "@/components/Discover/Map/Filter/ActAndSeq.vue";
import Attributes from "@/components/Discover/Map/Filter/AttributesFilter.vue";
import Funnel from "@/components/Discover/Map/Filter/FunnelFilter.vue";
import Trace from "@/components/Discover/Map/Filter/TraceFilter.vue";
import Timeframes from "@/components/Discover/Map/Filter/TimeframesFilter.vue";
import emitter from "@/utils/emitter";
import getMoment from "moment";

const props = defineProps([
  "sidebarFilter",
  "filterTasks",
  "filterStartToEnd",
  "filterEndToStart",
  "filterTimeframe",
  "filterTrace",
]);
const emit = defineEmits(["submit-all"]);
const $toast = useToast();

const loadingStore = useLoadingStore();
const allMapDataStore = useAllMapDataStore();
const { isLoading } = storeToRefs(loadingStore);
const {
  hasResultRule,
  temporaryData,
  postRuleData,
  ruleData,
  isRuleData,
  selectTimeFrame,
} = storeToRefs(allMapDataStore);

const selectFilter = {
  "Filter Type": ["Sequence", "Attributes", "Trace", "Timeframes"],
  // 'Filter Type': ['Sequence', 'Trace', 'Timeframes'],  // without Attributes
  "Activity Sequence": ["Have activity(s)", "Start & End", "Sequence"],
  "Start & End": ["Start", "End", "Start & End"],
  Mode: ["Directly follows", "Eventually follows"],
  ModeAtt: ["Case", "Activity"],
  Refine: ["Include", "Exclude"],
  Containment: ["Contained in", "Started in", "Ended in", "Active in"],
};
const tab = ref("filter"); // filter | funnel
const selectValue = reactive({
  0: "Sequence",
  1: "Have activity(s)",
  2: "Start",
  3: "Directly follows",
  4: "Case",
  5: "Include",
  6: "Contained in",
});
const selectFilterTask = ref(null);
const selectAttType = ref("");
const selectAttribute = ref(null);
const selectFilterStart = ref(null);
const selectFilterEnd = ref(null);
const selectFilterStartToEnd = ref(null);
const selectFilterEndToStart = ref(null);
const listSeq = ref([]);
// If start is selected first, end updates accordingly; if end is selected first, start updates accordingly
const isStartSelected = ref(null);
const isEndSelected = ref(null);
const isActAllTask = ref(true);
const rowData = ref([]);
const selectTraceArea = ref([]); // Trace slider
const filterTraceViewRef = ref(null);
const tooltip = {
  containment: {
    value:
      '<img src="/filterContainment.png" alt="filterContainment" class="block" width="108" height="250">',
    escape: false,
    autoHide: false,
    pt: {
      root: {
        class: "bg-transparent",
      },
      arrow: {
        class: "hidden",
      },
      text: {
        class: "p-0 bg-transparent",
      },
    },
  },
};

// All Task
const filterAllTaskData = computed(() => {
  return setHaveAct([...props.filterTasks]);
});

// Act And Seq
const filterTaskData = computed(() => {
  return setHaveAct([...props.filterTasks]);
});

// Start and End Task
const filterStartData = computed(() => {
  return setActData(props.filterStartToEnd);
});

const filterEndData = computed(() => {
  return setActData(props.filterEndToStart);
});

const filterStartToEndData = computed(() => {
  return isEndSelected.value
    ? setStartAndEndData(props.filterEndToStart, rowData.value, "sources")
    : setActData(props.filterStartToEnd);
});

const filterEndToStartData = computed(() => {
  return isStartSelected.value
    ? setStartAndEndData(props.filterStartToEnd, rowData.value, "sinks")
    : setActData(props.filterEndToStart);
});

// Apply Button disabled setting
const isDisabledButton = computed(() => {
  let disabled = true;
  const sele = selectValue;
  const type = selectAttType.value;
  const firstSelection = sele[0];

  if (firstSelection === "Sequence") {
    disabled = handleSequenceSelection(sele);
  } else if (firstSelection === "Attributes") {
    disabled = handleAttributesSelection(type);
  } else if (firstSelection === "Trace") {
    disabled = handleTraceSelection();
  } else if (firstSelection === "Timeframes") {
    disabled = handleTimeframesSelection();
  }

  return disabled;
});

/**
 * Change Radio Filter Type
 */
/** Checks if the apply button should be disabled based on filter type. */
function radioFilterType() {
  reset();
}

/**
 * Change Radio Act Seq
 */
/** Checks if the apply button should be disabled for activity sequence. */
function radioActSeq() {
  reset();
}

/**
 * Change Radio Start And End
 */
/** Checks if the apply button should be disabled for start & end filter. */
function radioStartAndEnd() {
  reset();
}

/**
 * @param {string} switch Summary or Insight
 */
function switchTab(newTab) {
  tab.value = newTab;
}

/**
 * Number to percentage
 * @param   {number} val - The raw ratio value.
 * @returns {string} The formatted percentage string.
 */
function getPercentLabel(val) {
  if (Number((val * 100).toFixed(1)) >= 100) return `100%`;
  else return `${(val * 100).toFixed(1)}%`;
}

/**
 * set progress bar width
 * @param   {number} value - The percentage value.
 * @returns {string} The CSS width style string.
 */
function progressWidth(value) {
  return `width:${value}%;`;
}

// Set up the Have activity(s) content
/**
 * @param {array} data filterTaskData
 */
function setHaveAct(data) {
  return data
    .map((task) => {
      return {
        label: task.label,
        occ_value: Number(task.occurrence_ratio * 100),
        occurrences: Number(task.occurrences).toLocaleString("en-US"),
        occurrences_base: task.occurrences,
        occurrence_ratio: getPercentLabel(task.occurrence_ratio),
        case_value: Number(task.case_ratio * 100),
        cases: task.cases.toLocaleString("en-US"),
        cases_base: task.cases,
        case_ratio: getPercentLabel(task.case_ratio),
      };
    })
    .sort((x, y) => y.occurrences_base - x.occurrences_base);
}

// Adjust the content of filterStartData / filterEndData / filterStartToEndData / filterEndToStartData
/**
 * @param {Array} array - filterStartToEnd or filterEndToStart data array.
 */
function setActData(array) {
  const list = [];
  array.forEach((task, index) => {
    const data = {
      label: task.label,
      occ_value: Number(task.occurrence_ratio * 100),
      occurrences: task.occurrences.toLocaleString("en-US"),
      occurrences_base: task.occurrences,
      occurrence_ratio: getPercentLabel(task.occurrence_ratio),
    };
    list.push(data);
  });
  list.sort((x, y) => y.occurrences_base - x.occurrences_base);
  return list;
}

/**
 * @param {array} e select Attribute
 */
function getSelectAttribute(e) {
  selectAttType.value = e.type;
  selectAttribute.value = e.data;
}

/**
 * @param {array} select select Have activity(s) rows
 */
function onRowAct(select) {
  selectFilterTask.value = select;
}

/**
 * @param {object} e select Start rows
 */
function onRowStart(e) {
  selectFilterStart.value = e.data;
}

/**
 * @param {object} e select End rows
 */
function onRowEnd(e) {
  selectFilterEnd.value = e.data;
}

/**
 * @param {array} newListSeq Update List Seq
 */
function onUpdateListSeq(newListSeq) {
  listSeq.value = newListSeq;
  isActAllTask.value = false;
}

// In Start & End mode, if start is selected first, end updates accordingly; if end is selected first, start updates accordingly
/**
 * @param {object} e object contains selected row's data
 */
function startRow(e) {
  selectFilterStartToEnd.value = e.data;
  if (isStartSelected.value === null || isStartSelected.value === true) {
    isStartSelected.value = true;
    isEndSelected.value = false;
    rowData.value = e.data;
  }
}

/**
 * @param {object} e object contains selected row's data
 */
function endRow(e) {
  selectFilterEndToStart.value = e.data;
  if (isEndSelected.value === null || isEndSelected.value === true) {
    isEndSelected.value = true;
    isStartSelected.value = false;
    rowData.value = e.data;
  }
}

// Reset the linked filterStartToEndData / filterEndToStartData content
/**
 * @param {array} eventData  Start or End List
 * @param {object} rowDataVal - The selected row's data.
 * @param {string} event sinks / sources
 */
function setStartAndEndData(eventData, rowDataVal, event) {
  const filterData =
    event === "sinks" ? props.filterEndToStart : props.filterStartToEnd;
  const relatedItems = eventData
    .find((task) => task.label === rowDataVal.label)
    ?.[event]?.filter((item) => filterData.some((ele) => ele.label === item))
    ?.map((item) => filterData.find((ele) => ele.label === item));

  if (!relatedItems) return [];
  return relatedItems.map((item) => ({
    label: item.label,
    occ_value: Number(item.occurrence_ratio * 100),
    occurrences: Number(item.occurrences).toLocaleString("en-US"),
    occurrence_ratio: getPercentLabel(item.occurrence_ratio),
  }));
}

/**
 * @param {object} e task's object
 */
/**
 * Creates a display rule object from filter event data.
 * @param {object} e - The filter rule data.
 * @returns {object} The display rule with type, label, and toggle.
 */
function setRule(e) {
  let label, type;
  const includeStr = e.is_exclude ? "exclude" : "include";
  const containmentMap = {
    "occurred-in": "contained in",
    "started-in": "started in",
    "completed-in": "ended in",
    "occurred-around": "active in",
  };

  switch (
    e.type //sonar-qube
  ) {
    case "contains-task":
    case "starts-with":
    case "ends-with":
    case "directly-follows":
    case "eventually-follows":
      label = `${includeStr}, ${getTaskLabel(e)}`;
      type = "Sequence";
      break;
    case "start-end":
      label = `${includeStr}, start with ${e.starts_with}, end with ${e.ends_with}`;
      type = "Sequence";
      break;
    case "trace-freq":
      label = `${includeStr}, from #${e.lower} to #${e.upper}`;
      type = "Trace";
      break;
    case "string-attr":
    case "boolean-attr":
    case "int-attr":
    case "float-attr":
    case "date-attr":
      label = `${includeStr}, ${getAttributeLabel(e)}`;
      type = "Attributes";
      break;
    case "occurred-in":
    case "started-in":
    case "completed-in":
    case "occurred-around":
      label = `${containmentMap[e.type]}, ${includeStr}, from ${getMoment(e.start).format("YYYY-MM-DD HH:mm")} to ${getMoment(e.end).format("YYYY-MM-DD HH:mm")} `;
      type = "Timeframe";
      break;
  }
  return {
    type,
    label,
    toggle: true,
  };
}

/**
 * @param {boolean} message - Whether to show the success toast message.
 */
function reset(message) {
  // Sequence
  selectFilterTask.value = null;
  selectFilterStart.value = null;
  selectFilterEnd.value = null;
  selectFilterStartToEnd.value = null;
  selectFilterEndToStart.value = null;
  listSeq.value = [];
  isStartSelected.value = null;
  isEndSelected.value = null;
  isActAllTask.value = true;
  // Attributes
  selectAttType.value = "";
  selectAttribute.value = null;
  emitter.emit("map-filter-reset", true);
  // Timeframes
  selectTimeFrame.value = [];
  // Trace
  if (filterTraceViewRef.value) {
    filterTraceViewRef.value.showTraceId = null;
    filterTraceViewRef.value.selectArea = [
      0,
      filterTraceViewRef.value.traceTotal,
    ];
  }
  // Success message
  if (message) {
    $toast.success("Filter cleared.");
  }
}

/**
 * Submits the selected filter data to the backend.
 */
async function submit() {
  isLoading.value = true;

  const sele = selectValue;
  const isExclude = sele[5] === "Exclude";
  const containmentMap = {
    "Contained in": "occurred-in",
    "Started in": "started-in",
    "Ended in": "completed-in",
    "Active in": "occurred-around",
  };

  const data = getFilterData(sele, isExclude, containmentMap);

  const postData = Array.isArray(data) ? data : [data];

  postRuleData.value = postData;
  await allMapDataStore.checkHasResult();

  if (hasResultRule.value === null) {
    isLoading.value = false;
  } else if (hasResultRule.value) {
    updateRules(postData);
    reset(false);
    await delay(1000);
    isLoading.value = false;
    $toast.success("Filter applied. Go to Funnel to verify.");
  } else {
    reset(false);
    await delay(1000);
    isLoading.value = false;
    $toast.warning("No result.");
  }
}

/**
 * create map
 */
/** Triggers the submit-all event to create the map. */
function sumbitAll() {
  emit("submit-all");
}

/**
 * Checks if the sequence filter selection is valid.
 * @param {Array} sele - The current selection values.
 * @returns {boolean} Whether the button should be disabled.
 */
function handleSequenceSelection(sele) {
  const secondSelection = sele[1];

  switch (secondSelection) {
    case "Have activity(s)":
      return !(selectFilterTask.value && selectFilterTask.value.length !== 0);
    case "Start & End":
      return handleStartEndSelection(sele[2]);
    case "Sequence":
      return listSeq.value.length < 2;
    default:
      return true;
  }
}

/**
 * Checks if the start & end sub-selection is valid.
 * @param {string} option - 'Start', 'End', or 'Start & End'.
 * @returns {boolean} Whether the button should be disabled.
 */
function handleStartEndSelection(option) {
  switch (option) {
    case "Start":
      return !selectFilterStart.value;
    case "End":
      return !selectFilterEnd.value;
    case "Start & End":
      return !(selectFilterStartToEnd.value && selectFilterEndToStart.value);
    default:
      return true;
  }
}

/**
 * Checks if the attribute filter selection is valid.
 * @param {string} type - The attribute type.
 * @returns {boolean} Whether the button should be disabled.
 */
function handleAttributesSelection(type) {
  switch (type) {
    case "string":
      return !(selectAttribute.value && selectAttribute.value.length > 0);
    case "boolean":
      return !(selectAttribute.value?.key && selectAttribute.value?.label);
    case "int":
    case "float":
    case "date":
      return !(
        selectAttribute.value?.key &&
        selectAttribute.value?.min !== null &&
        selectAttribute.value?.max !== null
      );
    default:
      return true;
  }
}

/** Checks if the trace filter selection is valid.
 * @returns {boolean} Whether the button should be disabled.
 */
function handleTraceSelection() {
  return selectTraceArea.value[0] === selectTraceArea.value[1];
}

/** Checks if the timeframes filter selection is valid.
 * @returns {boolean} Whether the button should be disabled.
 */
function handleTimeframesSelection() {
  return selectTimeFrame.value.length === 0;
}

/**
 * Gets the display label for a task filter rule.
 * @param {object} e - The filter rule data.
 * @returns {string} The formatted label.
 */
function getTaskLabel(e) {
  switch (e.type) {
    case "contains-task":
      return `${e.task}`;
    case "starts-with":
      return `start with ${e.task}`;
    case "ends-with":
      return `end with ${e.task}`;
    case "directly-follows":
    case "eventually-follows":
      return `${e.type.replace("-", " ")}, ${e.task_seq.join(" -> ")}`;
  }
}

/**
 * Gets the display label for an attribute filter rule.
 * @param {object} e - The filter rule data.
 * @returns {string} The formatted label.
 */
function getAttributeLabel(e) {
  switch (e.type) {
    case "string-attr":
      return `${e.key}, ${e.value}`;
    case "boolean-attr":
      return `${e.key}, ${selectAttribute.value?.label}`;
    case "int-attr":
    case "float-attr":
      return `${e.key}, from ${e.min} to ${e.max}`;
    case "date-attr":
      return `${e.key}, from ${getMoment(e.min).format("YYYY-MM-DD HH:mm")} to ${getMoment(e.max).format("YYYY-MM-DD HH:mm")}`;
  }
}

/**
 * Builds filter data from the current selections.
 * @param {Array} sele - The current selection values.
 * @param {boolean} isExclude - Whether this is an exclude filter.
 * @param {object} containmentMap - Containment type mapping.
 * @returns {object|Array} The filter data to submit.
 */
function getFilterData(sele, isExclude, containmentMap) {
  switch (sele[0]) {
    case "Sequence":
      return getSequenceData(sele, isExclude);
    case "Attributes":
      return getAttributesData(isExclude);
    case "Trace":
      return getTraceData(isExclude);
    case "Timeframes":
      return {
        type: containmentMap[sele[6]],
        start: selectTimeFrame.value[0],
        end: selectTimeFrame.value[1],
        is_exclude: isExclude,
      };
    default:
      return null;
  }
}

/**
 * Builds sequence filter data.
 * @param {Array} sele - The current selection values.
 * @param {boolean} isExclude - Whether this is an exclude filter.
 * @returns {object} The sequence filter data.
 */
function getSequenceData(sele, isExclude) {
  switch (sele[1]) {
    case "Have activity(s)":
      return selectFilterTask.value.map((task) => ({
        type: "contains-task",
        task: task.label,
        is_exclude: isExclude,
      }));
    case "Start & End":
      return getStartEndData(sele, isExclude);
    case "Sequence":
      return {
        type:
          sele[3] === "Directly follows"
            ? "directly-follows"
            : "eventually-follows",
        task_seq: listSeq.value.map((task) => task.label),
        is_exclude: isExclude,
      };
    default:
      return null;
  }
}

/**
 * Builds start & end filter data.
 * @param {Array} sele - The current selection values.
 * @param {boolean} isExclude - Whether this is an exclude filter.
 * @returns {object} The start & end filter data.
 */
function getStartEndData(sele, isExclude) {
  switch (sele[2]) {
    case "Start":
      return {
        type: "starts-with",
        task: selectFilterStart.value.label,
        is_exclude: isExclude,
      };
    case "End":
      return {
        type: "ends-with",
        task: selectFilterEnd.value.label,
        is_exclude: isExclude,
      };
    case "Start & End":
      return {
        type: "start-end",
        starts_with: selectFilterStartToEnd.value.label,
        ends_with: selectFilterEndToStart.value.label,
        is_exclude: isExclude,
      };
    default:
      return null;
  }
}

/**
 * Builds attributes filter data.
 * @param {boolean} isExclude - Whether this is an exclude filter.
 * @returns {object} The attributes filter data.
 */
function getAttributesData(isExclude) {
  const attrTypeMap = {
    string: "string-attr",
    boolean: "boolean-attr",
    int: "int-attr",
    float: "float-attr",
    date: "date-attr",
  };

  switch (selectAttType.value) {
    case "string":
      return selectAttribute.value.map((task) => ({
        type: attrTypeMap["string"],
        key: task.key,
        value: task.value,
        is_exclude: isExclude,
      }));
    case "boolean":
    case "int":
    case "float":
    case "date":
      return {
        type: attrTypeMap[selectAttType.value],
        key: selectAttribute.value.key,
        value: selectAttribute.value.value,
        min: selectAttribute.value.min,
        max: selectAttribute.value.max,
        is_exclude: isExclude,
      };
    default:
      return null;
  }
}

/**
 * Builds trace filter data.
 * @param {boolean} isExclude - Whether this is an exclude filter.
 * @returns {object} The trace filter data.
 */
function getTraceData(isExclude) {
  const lowerIndex = filterTraceViewRef.value.selectArea[0];
  const upperIndex = filterTraceViewRef.value.selectArea[1] - 1;
  return {
    type: "trace-freq",
    lower: allMapDataStore.traces[lowerIndex].id,
    upper: allMapDataStore.traces[upperIndex].id,
    is_exclude: isExclude,
  };
}

/**
 * Updates the rules list with newly submitted filter data.
 * @param {Array} postData - The submitted filter data.
 */
function updateRules(postData) {
  if (temporaryData.value?.length) {
    temporaryData.value.push(...postData);
    isRuleData.value.push(...postData);
    ruleData.value.push(...postData.map((e) => setRule(e)));
  } else {
    temporaryData.value.push(...postData);
    isRuleData.value = Array.from(temporaryData.value);
    ruleData.value = isRuleData.value.map((e) => setRule(e));
  }
}

/**
 * Returns a promise that resolves after the given delay.
 * @param {number} ms - The delay in milliseconds.
 * @returns {Promise} Resolves after the delay.
 */
function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
</script>

<style scoped>
#searchFiles::-webkit-search-cancel-button {
  appearance: none;
}
</style>
