<template>
  <section
    class="h-screen-main w-full px-4 flex flex-col justify-between items-start"
  >
    <!-- Upload Content -->
    <div class="w-full h-[calc(100%_-_64px)]">
      <!-- File name -->
      <!-- cursor-pointer -->
      <div class="h-12 pl-1 border-b border-neutral-300 flex items-center">
        <InputText
          type="text"
          v-model="fileName"
          id="fileNameInput"
          class="rounded !border-transparent !font-bold !text-base !py-1 min-w-[1px] w-auto"
          @input="onInput"
          @blur="onBlur"
          maxlength="200"
          title="Rename"
        />
      </div>
      <!-- Upload notification -->
      <div class="flex justify-start items-center space-x-2 ml-2 py-2">
        <span
          class="material-symbols-outlined text-neutral-700 cursor-pointer"
          v-tooltip.right="tooltipUpload"
          >info</span
        >
        <span class="w-px h-7 bg-neutral-300"></span>
        <!-- Upload text -->
        <div>
          <div v-if="!isDisabled"></div>
          <div
            v-else
            class="flex justify-start items-center space-x-2 duration-700"
          >
            <p v-if="informData.length !== 0" class="text-primary text-sm">
              Need to select
              <span v-for="(item, index) in informData" :key="index"
                >{{ item.label
                }}<span v-if="index !== informData.length - 1">, </span></span
              >.
            </p>
            <div v-if="repeatedData.length !== 0" class="duration-700">
              <p v-if="repeatedData.length === 1" class="text-danger text-sm">
                {{ repeatedData[0].label }} has been assigned.
              </p>
              <p v-else class="text-danger text-sm">
                <span v-for="(item, index) in repeatedData" :key="index"
                  >{{ item.label
                  }}<span v-if="index !== repeatedData.length - 1"
                    >,
                  </span></span
                >
                have been assigned.
              </p>
            </div>
          </div>
        </div>
      </div>
      <!-- Upload table -->
      <div
        class="overflow-y-auto overflow-x-auto scrollbar max-h-[calc(100%_-_94px)]"
      >
        <table
          class="text-sm border-separate border-spacing-0 h-full overflow-y-auto overflow-x-auto scrollbar"
        >
          <caption class="hidden">
            Upload
          </caption>
          <thead class="sticky top-0 bg-neutral-10">
            <tr class="hidden">
              <th></th>
            </tr>
            <tr>
              <td
                v-for="(item, index) in uploadDetail?.columns"
                :key="index"
                class="border border-neutral-500 p-2 truncate max-w-[198px]"
              >
                {{ item }}
              </td>
            </tr>
            <tr>
              <td
                v-for="(item, index) in uploadDetail?.columns"
                :key="index"
                class="px-2 py-1 bg-neutral-300 border border-neutral-500 max-w-[198px]"
              >
                <Select
                  v-model="selectedColumns[index]"
                  :options="columnType"
                  optionLabel="name"
                  placeholder=""
                  class="w-[180px] !border-neutral-500"
                  :data-type="item"
                  :inputId="index.toString()"
                  :inputClass="[selectedColumns[index]?.color, 'text-sm']"
                >
                  <template #option="slotProps">
                    <div :class="slotProps.option.color" class="text-sm">
                      <span>{{ slotProps.option.name }}</span>
                    </div>
                  </template>
                </Select>
              </td>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(item, index) in uploadDetail?.data" :key="index">
              <td
                v-for="(itemDetail, key) in item"
                :key="key"
                class="border border-neutral-500 p-2 truncate max-w-[198px]"
              >
                {{ itemDetail }}
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    <!-- Upload button -->
    <div class="w-full text-right space-x-4 px-8 py-4">
      <button type="button" class="btn btn-sm btn-neutral" @click="cancel">
        Cancel
      </button>
      <button type="button" class="btn btn-sm btn-neutral" @click="reset">
        Reset
      </button>
      <button
        type="button"
        class="btn btn-sm"
        @click="submit"
        :disabled="isDisabled"
        :class="isDisabled ? 'btn-disable' : 'btn-neutral'"
      >
        Upload
      </button>
    </div>
  </section>
</template>
<script>
// The Lucia project.
// Copyright 2023-2026 DSP, inc.  All rights reserved.
// Authors:
//   chiayin.kuo@dsp.im (chiayin), 2023/1/31
//   cindy.chang@dsp.im (Cindy Chang), 2024/5/30
//   imacat.yang@dsp.im (imacat), 2023/9/23
/**
 * @module views/Upload File upload page with column mapping
 *   dropdowns, file rename input, and validation before final
 *   upload submission.
 */

import { useFilesStore } from "@/stores/files";

export default {
  beforeRouteEnter(to, from, next) {
    // An uploadID is required to enter this page
    next((vm) => {
      const filesStore = useFilesStore();
      if (filesStore.uploadId === null) {
        vm.$router.push({ name: "Files", replace: true });
        vm.$toast.default("Please upload your file.", { position: "bottom" });
      }
    });
  },
};
</script>
<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
import { useRouter } from "vue-router";
import { storeToRefs } from "pinia";
import { useLoadingStore } from "@/stores/loading";
import {
  uploadConfirm,
} from "@/module/alertModal.js";

const router = useRouter();

// Stores
const loadingStore = useLoadingStore();
const filesStore = useFilesStore();
const { isLoading } = storeToRefs(loadingStore);
const { uploadDetail, uploadId, uploadFileName } = storeToRefs(filesStore);

// Data
const tooltipUpload = {
  value: `1. Case ID: A unique identifier for each case.
    2. Activity: A process step executed by either a system (automated) or humans (manual).
    3. Activity Instance ID: A unique identifier for a single occurrence of an activity.
    4. Timestamp: The time of occurrence of a particular event, such as the start or end of an activity.
    5. Status: Activity status, such as Start or Complete.
    6. Attribute: A property that can be associated with a case to provide additional information about that case.`,
  // Resource is not available yet
  // 7. Resource: A resource refers to any entity that is required to carry out a business process. This can include people, equipment, software, or any other type of asset.
  class: "!max-w-[400px] !text-[10px] !opacity-80",
  autoHide: false,
};

const columnType = [
  {
    name: "Case ID*",
    code: "case_id",
    color: "!text-secondary",
    value: "",
    label: "Case ID",
    required: true,
  },
  {
    name: "Timestamp*",
    code: "timestamp",
    color: "!text-secondary",
    value: "",
    label: "Timestamp",
    required: true,
  },
  {
    name: "Status*",
    code: "status",
    color: "!text-secondary",
    value: "",
    label: "Status",
    required: true,
  },
  {
    name: "Activity*",
    code: "name",
    color: "!text-secondary",
    value: "",
    label: "Activity",
    required: true,
  },
  {
    name: "Activity Instance ID*",
    code: "instance",
    color: "!text-secondary",
    value: "",
    label: "Activity Instance ID",
    required: true,
  },
  {
    name: "Case Attribute",
    code: "case_attributes",
    color: "!text-primary",
    value: "",
    label: "Case Attribute",
    required: false,
  },
  // { name: 'Resource', code: '', color: '', value: '', label: 'Resource', required: false }, // Not available yet; may be added in the future
  {
    name: "Not Assigned",
    code: "",
    color: "!text-neutral-700",
    value: "",
    label: "Not Assigned",
    required: false,
  },
];

const selectedColumns = ref([]);
const informData = ref([]);
const repeatedData = ref([]);
const fileName = ref(uploadFileName.value);
const showEdit = ref(false);

// Computed
const isDisabled = computed(() => {
  // 1. Length must match; every column must be assigned
  // 2. Must not be null or undefined
  const hasValue = !selectedColumns.value.includes(undefined);
  const result = !(
    selectedColumns.value.length === uploadDetail.value?.columns.length &&
    informData.value.length === 0 &&
    repeatedData.value.length === 0 &&
    hasValue
  );
  return result;
});

// Watch
watch(
  selectedColumns,
  (newVal) => {
    updateValidationData(newVal);
  },
  { deep: true },
);

// Methods
/**
 * Adjusts input width and trims whitespace on blur.
 * @param {Event} e - The blur event from the file name input.
 */
function onBlur(e) {
  const baseWidth = 20;

  if (e.target.value === "") {
    e.target.value = uploadFileName.value;
    const textWidth = getTextWidth(e.target.value, e.target);
    e.target.style.width = baseWidth + textWidth + "px";
  } else if (e.target.value !== e.target.value.trim()) {
    e.target.value = e.target.value.trim();
    const textWidth = getTextWidth(e.target.value, e.target);
    e.target.style.width = baseWidth + textWidth + "px";
  }
}

/**
 * Dynamically resizes the input width as the user types.
 * @param {Event} e - The input event from the file name input.
 */
function onInput(e) {
  const baseWidth = 20;
  const textWidth = getTextWidth(e.target.value, e.target);

  e.target.style.width = baseWidth + textWidth + "px";
}

/**
 * Measures the pixel width of text using a hidden span element.
 * @param {string} text - The text to measure.
 * @param {HTMLElement} e - The input element for font style reference.
 * @returns {number} The text width in pixels.
 */
function getTextWidth(text, e) {
  // Replace spaces with non-breaking spaces
  const processedText = text.replaceAll(" ", "\u00a0");
  const hiddenSpan = document.createElement("span");

  hiddenSpan.textContent = processedText;
  hiddenSpan.style.font = globalThis.getComputedStyle(e).font;
  hiddenSpan.style.visibility = "hidden";
  document.body.appendChild(hiddenSpan);
  const width = hiddenSpan.getBoundingClientRect().width;
  hiddenSpan.remove();

  return width;
}

/**
 * Updates validation state (missing required and duplicate columns).
 * @param {Array} data - The currently selected column type assignments.
 */
function updateValidationData(data) {
  const nameOccurrences = {};
  const noSortedRepeatedData = []; // Unsorted duplicate selections
  const selectedData = []; // Already selected data

  informData.value = []; // Not yet selected data
  repeatedData.value = []; // Duplicate selections

  data.forEach((item) => {
    const { name, code } = item;

    if (nameOccurrences[name]) {
      // 'Not Assigned' and 'Case Attribute' are excluded from validation
      if (!code || code === "case_attributes") return;
      nameOccurrences[name]++;
      // Each duplicate option should only appear once
      if (nameOccurrences[name] === 2) {
        noSortedRepeatedData.push(item);
      }
      // Sort according to the dropdown menu order
      repeatedData.value = columnType.filter((column) =>
        noSortedRepeatedData.includes(column),
      );
    } else {
      nameOccurrences[name] = 1;
      selectedData.push(name);
      informData.value = columnType.filter((item) =>
        item.required ? !selectedData.includes(item.name) : false,
      );
    }
  });
}

/** Resets all column selections to default. */
function reset() {
  // Do not add to browser history
  selectedColumns.value = [];
}

/** Navigates back to the Files page without uploading. */
function cancel() {
  // Do not add to browser history
  router.push({ name: "Files", replace: true });
}

/** Submits the column mapping and triggers the second-stage upload. */
async function submit() {
  // Post API Data
  const fetchData = {
    timestamp: "",
    case_id: "",
    name: "",
    instance: "",
    status: "",
    case_attributes: [],
  };
  // Assign values
  const haveValueData = selectedColumns.value.map((column, i) => {
    if (column && uploadDetail.value?.columns?.[i]) {
      return {
        name: column.name,
        code: column.code,
        color: column.color,
        value: uploadDetail.value.columns[i],
      };
    }
  });

  // Get the desired file name to change
  uploadFileName.value = fileName.value;
  // Set the data for the second-stage upload
  haveValueData.forEach((column) => {
    if (column !== undefined) {
      switch (column.code) {
        case "timestamp":
          fetchData.timestamp = column.value;
          break;
        case "case_id":
          fetchData.case_id = column.value;
          break;
        case "name":
          fetchData.name = column.value;
          break;
        case "instance":
          fetchData.instance = column.value;
          break;
        case "status":
          fetchData.status = column.value;
          break;
        case "case_attributes":
          fetchData.case_attributes.push(column.value);
          break;
        default:
          break;
      }
    }
  });
  uploadConfirm(fetchData);
}

// Mounted
onMounted(async () => {
  // Watch only once
  const unwatch = watch(
    fileName,
    (newValue) => {
      if (newValue) {
        const inputElement = document.getElementById("fileNameInput");
        if (!inputElement) return;
        const baseWidth = 20;
        const textWidth = getTextWidth(fileName.value, inputElement);
        inputElement.style.width = baseWidth + textWidth + "px";
      }
    },
    { immediate: true },
  );
  showEdit.value = true;
  if (uploadId.value) await filesStore.getUploadDetail();
  if (uploadDetail.value?.columns) {
    selectedColumns.value = Array.from(
      { length: uploadDetail.value.columns.length },
      () => columnType[columnType.length - 1],
    ); // Default to "Not Assigned"
  }
  unwatch();
  isLoading.value = false;
});

onBeforeUnmount(() => {
  // Clear uploadID when leaving the page
  uploadId.value = null;
  uploadFileName.value = null;
});
</script>
