<template>
  <field class="file-upload" v-bind="$props">
    <div
      v-if="!modelValue || !modelValue.length"
      class="file-upload__drop-area"
      :class="classes"
      data-testid="file-upload-drop-area"
      @drop.prevent="onDropFile($event)"
      @dragover.prevent
      @click="onInputClick"
    >
      Glissez votre fichier ici ou cliquez pour parcourir
      <input
        ref="inputFile"
        role="input"
        data-testid="file-upload-input-file"
        type="file"
        :accept="accept"
        @change="onSelectFile($event)"
      />
    </div>

    <div
      v-if="modelValue?.length"
      class="file-upload__file-list"
      :class="classes"
    >
      <div
        v-for="file in modelValue"
        :key="file.name"
        class="file-upload__file"
        :class="{ wide }"
      >
        <img
          v-if="isFileImage(file)"
          :src="getImageSrc(file)"
          class="file-upload__file-img"
        />
        <span v-else class="file-upload__file-name">
          {{ file.name }}
        </span>
        <BmsIconButton
          :mode="StatusType.Danger"
          @click.prevent="onDeleteFile(file)"
          class="close-button"
        >
          <XCircle />
        </BmsIconButton>
      </div>
    </div>
  </field>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
import { XCircle } from 'lucide-vue-next';
import BmsIconButton from '../button/BmsIconButton.vue';
import { isFileImage, getImageFromFile } from '@/helpers/file.helper';
import { Caption } from '@/models/caption.model';
import { StatusType } from '@/models';
import { FieldComponentProps } from '@/plugins/field/field-component.model';

interface Props extends FieldComponentProps {
  modelValue: File[];
  limit?: number;
  placeholder?: string;
  wide?: boolean;
  accept?: string;
}

const isDragOver = ref(false);
const inputFile: Ref<HTMLInputElement | null> = ref(null);
const classes = computed(() => ({
  'is-active': isDragOver.value,
  'is-disabled': props.disabled,
  'is-error': !!props.errors?.length,
  wide: props.wide,
}));
const images: Ref<{ img: string; name: string }[]> = ref([]);

const props = withDefaults(defineProps<Props>(), {
  limit: 10,
  wide: false,
  accept: '',
});

const emit = defineEmits(['update:modelValue']);

onMounted(() => {
  window.addEventListener('dragover', onDragStartEvent);
  window.addEventListener('dragleave', onDragEndEvent);
  if (props.modelValue.length > 0) {
    updateFiles();
  }
});

onUnmounted(() => {
  window.removeEventListener('dragover', onDragStartEvent);
  window.removeEventListener('dragleave', onDragEndEvent);
});

function onDragStartEvent() {
  isDragOver.value = true;
}

function onDragEndEvent() {
  isDragOver.value = false;
}

const onInputClick = () => {
  if (inputFile.value) {
    inputFile.value.click();
  }
};

const handleFile = async (file: File) => {
  try {
    await checkSize(file);
    emit('update:modelValue', [file]);
  } catch (error) {
    alert(error);
  } finally {
    isDragOver.value = false;
  }
};

async function onSelectFile(event: Event) {
  const target = event?.target as HTMLInputElement;
  const file = target?.files?.[0];

  if (!file) {
    return;
  }
  handleFile(file);
}

function checkSize(file: File): Promise<boolean> {
  return new Promise((resolve, reject) => {
    if (file.size / 1048576 > props.limit) {
      reject(`Le fichier dépasse la limite autorisée de ${props.limit}Mo`);

      return;
    }

    resolve(true);
  });
}

async function onDropFile(event: DragEvent) {
  if (!event.dataTransfer?.files) {
    return;
  }

  const file = event.dataTransfer?.files[0];
  handleFile(file);
}

function onDeleteFile(file: File) {
  emit(
    'update:modelValue',
    props.modelValue.filter((f) => f !== file),
  );
}

const getImageSrc = (file: File): string => {
  const image = images.value.find((image) => image.name === file.name);
  return image ? image.img : '';
};

const updateFiles = async () => {
  try {
    images.value = [];
    const promises: any[] = [];
    props.modelValue?.forEach((file) => {
      if (isFileImage(file)) {
        promises.push(getImageFromFile(file));
      }
    });
    images.value = await Promise.all(promises);
  } catch (err) {
    console.error(err);
  }
};

watch(
  () => props.modelValue,
  () => {
    updateFiles();
  },
);
</script>

<style lang="scss" scoped>
.file-upload {
  --drop-color: var(--bms-grey-10);
  --drop-border: 1px dashed var(--bms-grey-50);

  :deep(.field__wrapper) {
    width: 100%;
    min-width: 0;
  }

  .is-disabled {
    --drop-color: var(--bms-field-disabled-background);
    --drop-border: 1px solid var(--bms-field-disabled-border-color);
    color: var(--bms-field-disabled-text-color);
    pointer-events: none;
  }
  .is-error {
    --drop-color: var(--bms-red-25);
    --drop-border: 1px solid var(--bms-red-100);
  }

  &__drop-area {
    display: block;
    padding: calc(4 * var(--field-padding)) var(--field-padding);
    text-align: center;
    border-radius: var(--bms-border-radius);
    border: var(--drop-border);
    background: var(--drop-color);
    cursor: pointer;

    &:active,
    &__active {
      --drop-color: transparent;
      --drop-border: 1px solid var(--bms-main-100);
    }

    &:hover,
    &__hover {
      --drop-color: var(--bms-main-10);
      --drop-border: 1px dashed var(--bms-grey-100);
    }

    input[type='file'] {
      opacity: 0;
      position: absolute;
      pointer-events: none;
      left: 0;
      width: 0;
      height: 0;
    }

    &.is-active {
      --drop-color: var(--bms-main-25);
    }

    &.wide {
      width: 100%;
    }
  }

  &__file-list {
    display: flex;
    width: 100%;
    gap: 1em;
  }

  &__file {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 96px;
    height: 96px;
    border-radius: var(--bms-border-radius);
    border: 1px solid var(--bms-grey-25);
    background: var(--bms-grey-10);
    position: relative;

    &.wide {
      width: 100%;
      height: 300px;
    }

    &-img {
      max-width: 100%;
      max-height: 100%;
    }

    &-name {
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
      flex: 1 1 auto;
    }

    :deep(.close-button) {
      position: absolute;
      top: 0;
      right: 0;
    }
  }
}
</style>
