<template>
  <div class="relative h-full">

    <div v-if="!dropZone" class="flex flex-col" @mouseenter="onFileUploadHover">
      <div class="flex">
        <AisButton :variant="variant"
                   :size="size"
                   icon="file" hover fast press
                   :disabled="disabled"
                   @click="handleFileUploadClick"
                   @mouseenter.prevent="onFileUploadHover"
        >{{ buttonTitle }}</AisButton>

        <div v-if="native" class="grow">
          <div class="flex items-center justify-end">
            <AisInput class="w-full"
                      placeholder="No file chosen"
                      :size="size"
                      disabled
                      v-model="nativeFileValue"
                      @click="handleFileUploadClick"
                      style="cursor: default">
            </AisInput>
            <AisIcon v-if="selectedFiles.length > 0" @click="resetSelectedFiles" :class="computeIconClass"
                     icon="cancel"></AisIcon>
          </div>
        </div>
      </div>
      <label class="flex text-sm" :class="{'justify-end': native}">{{ computeFileSize }}</label>
    </div>

    <Transition
        enter-active-class="transition opacity duration-300 ease"
        enter-from-class="transform opacity-0"
        leave-active-class="transition opacity duration-300 ease"
        leave-to-class="transform opacity-0"
    >
      <FileView v-if="showFileView" :variant="variant" ref="fileViewRef" class="z-40 absolute" :class="computeFileViewWidth" :files="selectedFiles"
                @remove="remove"></FileView>
    </Transition>


    <div v-if="dropZone" class="relative h-full"
         @dragenter.prevent="toggleActive"
         @dragleave.prevent="toggleActive"
         @dragover.prevent
         @drop.prevent="onFileSelect"
    >
      <div class="border-[20px] h-full rounded overflow-auto transition duration-200 ease-in-out"
           :class="computeToggleActiveStyle">
        <div v-if="selectedFiles.length === 0"
            class="flex flex-col gap-6 p-2 items-center justify-center h-full border border-ui-gray-900 border-dashed rounded border-opacity-25"
        >
          <label :class="{'opacity-30 cursor-not-allowed': disabled}">{{ dropTitle }}</label>
          <label :class="{'opacity-30 cursor-not-allowed': disabled}">OR</label>
          <AisButton :variant="variant"
                     :size="size"
                     icon="file" hover fast press
                     :disabled="disabled"
                     @click="handleFileUploadClick"
          >{{ buttonTitle }}</AisButton>
        </div>
        <FileView :variant="variant" :files="selectedFiles" @remove="remove"></FileView>
      </div>
    </div>
    <div v-if="errorMessage" class="ais-field-errors m-0 mt-1 pl-1.5 text-danger text-base flex items-center gap-2">
      <AisIcon icon="error"/>
      {{ errorMessage }}
    </div>

    <input type="file" @click="onFileClick" :accept="accept" :multiple="multiple" ref="file" class="hidden" @change="onFileSelect">
  </div>

</template>

<script setup>
import { ref, computed, watch, onMounted } from "vue";
import {FileUploadProps} from "@/components/fileupload/fileupload-props";
import AisButton from "@/components/button/AisButton.vue";
import AisInput from "@/components/input/AisInput.vue";
import AisIcon from "@/components/icon/AisIcon.vue";
import {borderVariantUtil, textVariantClassUtil,getFileSize} from "@/components/fileupload/fileupload-util.js";
import FileView from "@/components/fileupload/FileView.vue";
import {onClickOutside} from '@vueuse/core'

const emit = defineEmits(["select", "update:modelValue"])

const props = defineProps({
  ...FileUploadProps,
  modelValue: { type: Object, default: () => {} }
})

const file = ref(null);
const selectedFiles = ref([])
const nativeFileValue = ref(null)
const showFileView = ref(false)
const fileViewRef = ref();
const errorMessage = ref();

//drop zone
const active = ref(false);

onClickOutside(fileViewRef, () => showFileView.value = false)

const buttonTitle = computed(() => 'Choose File' + (props.multiple ? '(s)' : ''))
const dropTitle = computed(() => 'Drag or Drop File' + (props.multiple ? '(s)' : ''))

function onFileClick(){
  file.value.value = null
}

function handleFileUploadClick() {
  if (!props.disabled) {
    if(!props.multiple) resetSelectedFiles();
    file.value.click();
  }
}

function onFileUploadHover() {
  if (selectedFilesLength.value > 0) {
    showFileView.value = true
  }

}

function onFileSelect(event) {
  errorMessage.value = null
  let fileLst;
  if (props.dropZone && event instanceof DragEvent) {
    toggleActive();
    fileLst = event.dataTransfer.files
  } else {
    fileLst = event.target.files
  }
  buildSelectedFiles(fileLst)
  nativeFileValue.value = buildFileDisplayLabel(true);
  emitValues();

}

function buildSelectedFiles(fileList) {
  if (checkIfFileCanBeAddedInDropzone(fileList)) {
    for (const f of fileList) {
      if (!checkIfFileIsAlreadyPresent(f) && !props.disabled && checkAcceptFile(f)) {
        selectedFiles.value.push(f)
      }
    }
  } else {
    errorMessage.value = 'You have tried to upload multiple files. Only one file is allowed!'
  }
}

function checkIfFileIsAlreadyPresent(file) {
  for (const f of selectedFiles.value) {
    if (f.name === file.name) {
      return true;
    }
  }
  return false
}

function checkIfFileCanBeAddedInDropzone(fileList) {
  return props.multiple || fileList.length === 1
}

function checkAcceptFile(file) {
  if (props.accept === undefined) {
    return true
  }
  const acceptLst = props.accept.split(",");
  let fileName = file.name;
  const fileNameExt = "." + fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length)
  if (acceptLst.includes(fileNameExt)) {
    return true;
  }
  return false;
}

function buildFileDisplayLabel(displaySingleFileName) {
  if (selectedFilesLength.value > 0) {
    let initialFileName = '';
    if (displaySingleFileName) {
      initialFileName = selectedFiles.value[0].name
    } else {
      initialFileName = '1 file'
    }
    return selectedFilesLength.value === 1 ? initialFileName : `${selectedFilesLength.value} files`
  }
}

const selectedFilesLength = computed(() => selectedFiles.value.length)

function buildFileSize() {
  return getFileSize(selectedFiles.value, true)
}

function emitValues() {
  if (!props.multiple) {
    const emitValue = selectedFilesLength.value === 1 ? selectedFiles.value[0] : null
    emit("update:modelValue", emitValue)
    emit("select", emitValue)
  } else {
    emit("update:modelValue", selectedFiles.value)
    emit("select", selectedFiles.value)
  }
}

function remove(file) {
  const fileIndex = selectedFiles.value.indexOf(file);
  selectedFiles.value.splice(fileIndex, 1)
  if (selectedFilesLength.value === 0) {
    resetSelectedFiles();
  }
  nativeFileValue.value = buildFileDisplayLabel(true);
  emitValues();
}

function resetSelectedFiles() {
  selectedFiles.value = [];
  nativeFileValue.value = null;
  file.value.value = null
  emitValues();
}

function toggleActive() {
  active.value = !active.value
}


const computeIconClass = computed(() => {
  const classLst = [`absolute right-4 cursor-pointer ${textVariantClassUtil(props.variant)}`]
  switch (props.size) {
    case "sm":
      classLst.push("text-base");
      break;
    case "md":
      classLst.push("text-xl")
      break;
    case "lg":
      classLst.push("text-2xl")
      break;
  }
  return classLst;
})

const computeFileSize = computed(() => {
  if (selectedFilesLength.value > 0)
    return `${buildFileDisplayLabel(false)} (${buildFileSize()})`;
})

const computeToggleActiveStyle = computed(() => {
  const classLst = []
  if (active.value && !props.disabled) {
    classLst.push(`${borderVariantUtil(props.variant)}`)
  }
  if (props.disabled) {
    classLst.push("cursor-not-allowed")
  }
  return classLst;
})

const computeFileViewWidth = computed(() => {
  return props.native ? 'w-full' : 'w-1/2';
})

watch(() => props.modelValue, (newValue) => {
  if (!newValue) {
    selectedFiles.value = []
    if (props.native) {
      nativeFileValue.value = null
    }
  }
})

/* Added this for when AisFileUpload is in a modal or hidden/shown. Without this, v-model on the consuming component doesn't
get updated. For example, user adds file then closes modal before uploading, then reopens modal. This component resets to
 empty array on create but consuming component still has the file in its v-model. There may be a better way to handle this. */
onMounted(() => { emitValues() })


</script>