<script setup lang="ts">
import FileUpload from 'primevue/fileupload';
import VHint from '../hint/VHint.vue';
import VLabel from '../label/VLabel.vue';
import VButton from '../button/VButton.vue';
import VButtonGroup from '../button/VButtonGroup.vue';
import VFile from './VFile.vue';
import VMessage from '../message/VMessage.vue';
import VScrollPanel from '../scrollpanel/VScrollPanel.vue';
import VLabelErrorProxy from './VLabelErrorProxy.vue';
import type IVFileUpload from './IVFileUpload.type';
import type { FileUploadErrorEvent, FileUploadProgressEvent, FileUploadRemoveEvent, FileUploadSelectEvent, FileUploadUploadEvent } from 'primevue/fileupload';
import { computed, useId, ref } from 'vue';
import styles from '@visua/typography.module.css';

const fileUploadRef = ref();
const fileProgressMap = ref<Record<string, number>>({});
const hasActiveError = ref(false);

defineOptions({
  inheritAttrs: false,
})

const props = withDefaults(defineProps<IVFileUpload>(), {
  id: () => useId(),
  label: 'Ajouter un fichier',
  validMessage: '',
  hint: '',
  disabled: false,
  invalidFileSizeMessage: 'Taille de fichier {0} invalide, la taille du fichier doit être plus petite que {1}',
  invalidFileLimitMessage: 'Nombre maximal des fichiers atteint, le nombre maximal est {0.}',
  invalidFileTypeMessage: 'Type de fichier invalide, le type de fichier {0} n\'est pas pris en charge',
  accept: undefined,
  chooseLabel: 'Choisir',
  name: undefined,
  url: undefined,
  maxFileSize: undefined,
  fileLimit: undefined,
  withCredentials: false,
  previewWidth: 50,
  uploadLabel: 'Téléverser',
  cancelLabel: 'Annuler',
  showCancelButton: true,
  showUploadButton: true,
  advanced: false
})

const emit = defineEmits([
  'select',
  'before-upload',
  'progress',
  'upload',
  'error',
  'before-send',
  'clear',
  'remove',
  'removeUploadFile',
  'uploader',
]);

defineExpose({
  upload: () => fileUploadRef.value.upload()
});

const handleSelect = (event: FileUploadSelectEvent) => {
  emit('select', event);
  if (!props.advanced && event.files.length > 0 && fileUploadRef.value) {
    fileUploadRef.value.files = [event.files.at(-1)];
    fileUploadRef.value.uploadedFiles = [];
  }
};

const handleClear = () => {
  emit('clear');
  hasActiveError.value = false;
};

const handleUpload = (event: FileUploadUploadEvent) => {
  emit('upload', event);
};

const handleRemove = (event: FileUploadRemoveEvent) => {
  emit('remove', event);
  hasActiveError.value =false;
};

const handleProgress = (event: FileUploadProgressEvent) => {
  emit('progress', event);

  const files = fileUploadRef.value?.files || [];
  files.forEach((file: File) => {
    fileProgressMap.value[file.name] = event.progress;
  });
};

const handleError = (event: FileUploadErrorEvent) => {
  emit('error', event);
  hasActiveError.value = true;
  if (!props.advanced && fileUploadRef.value) {
    fileUploadRef.value.uploadedFiles = [];
  }
}

const lastSelectedFile = computed(() => {
  const files = fileUploadRef.value?.files || [];
  return files.at(-1) ?? null;
});

const uploadEvent = (callback: () => void) => {
  callback();
};

const padding = computed(() => props.advanced ? '1.125rem' : '0rem')
const borderColor = computed(() => props.advanced ? 'var(--border-default-grey)' : 'transparent');

const labelState = computed(() => {
  if(!hasActiveError.value && !props.disabled) return 'default';
  else if(hasActiveError.value && !props.disabled) return 'error';
  else return undefined
})

type MessageType = 'alert' | 'warning' | 'success' | 'info';

const globalStatusMessage = computed<{
  type: MessageType;
  title: string;
} | null>(() => {
  const files = fileUploadRef.value?.files || [];
  const uploaded = fileUploadRef.value?.uploadedFiles || [];

  const hasError = files.some((f: { status: string; }) => f.status === 'error');
  const hasPending = files.length > 0;

  if (hasError) return { type: 'alert', title: 'Erreur lors du téléversement' };
  if (hasPending) return { type: 'warning', title: 'En attente de téléversement' };
  if (uploaded.length > 0) return { type: 'success', title: 'Tous les fichiers ont été téléversés' };
  return null;
});

</script>

<template>
  <div class="container" data-testid="file-label">
    <VLabel
      :for="`file-upload-${props.id}`"
      :label="props.label"
      :disabled="props.disabled"
      :type="labelState"
      :hint="props.hint"
    />
    <FileUpload
      ref="fileUploadRef"
      :id="`file-upload-${props.id}`"
      :multiple="props.advanced"
      :invalid-file-limit-message="props.invalidFileLimitMessage"
      :invalid-file-size-message="props.invalidFileSizeMessage"
      :invalid-file-type-message="props.invalidFileTypeMessage"
      mode="advanced"
      :accept="props.accept"
      :disabled="props.disabled"
      v-bind="$attrs"
      :auto="!props.advanced"
      :choose-label="props.chooseLabel"
      :upload-label="props.uploadLabel"
      :cancel-label="props.cancelLabel"
      :name="props.name"
      :url="props.url"
      :max-file-size="props.maxFileSize"
      :file-limit="props.fileLimit"
      :with-credentials="props.withCredentials"
      :preview-width="props.previewWidth"
      :show-cancel-button="props.showCancelButton"
      :show-upload-button="props.showUploadButton"
      @select="handleSelect($event)"
      @before-send="emit('before-send', $event)"
      @progress="handleProgress($event)"
      @before-upload="emit('before-upload', $event)"
      @uploader="emit('uploader', $event)"
      @upload="handleUpload($event)"
      @error="handleError($event)"
      @clear="handleClear()"
      @remove="handleRemove($event)"
      @remove-uploaded-file="emit('removeUploadFile', $event)"
      class="p-fileupload"
    >
      <template #header="slotProps">
        <VButtonGroup
          v-if="props.advanced"
          :buttons="[
            {
              label: 'Parcourir...',
              onClick: slotProps.chooseCallback,
              title: 'parcourir les fichiers',
            },
            {
              label: 'Téléverser',
              icon: 'ri-upload-cloud-line',
              onClick: () => uploadEvent(slotProps.uploadCallback),
              secondary: true,
              disabled: !slotProps.files || slotProps.files.length === 0,
              title: 'televerser les fichiers'
            },
            {
              label: 'Supprimer',
              tertiary: true,
              onClick: slotProps.clearCallback,
              disabled: !slotProps.files || slotProps.files.length === 0,
              title: 'suprimer les fichiers'
            }
          ]"
          size="sm"
          :disabled="props.disabled"
          inlineLayoutWhen="always"
          title="boutons de gestion des fichiers"
        />
        <div v-else class="simple">
          <VButton
            label="Parcourir..."
            :disabled="props.disabled"
            size="sm"
            @click="slotProps.chooseCallback"
            title="parcourir les fichiers"
          />
          <span
            v-if="(!slotProps.files || slotProps.files.length === 0) && (!slotProps.uploadedFiles || slotProps.uploadedFiles.length === 0) && !hasActiveError"
            :class="[styles['text-body-SM-detail-text-Regular']]"
          >
            Aucun fichier sélectionné
          </span>
        </div>
      </template>
      <template #empty v-if="props.advanced && !hasActiveError">
        <div class="fileupload-empty" :class="[styles['text-body-SM-detail-text-Regular']]">
          <i class="ri-upload-cloud-line upload-cloud-icon"></i>
          <p>Glissez-déposez les fichiers ici pour les téléverser.</p>
        </div>
      </template>
      <template #content="slotProps">
        <div v-if="props.advanced && !hasActiveError" style="margin-top: 0.75rem;" class="file-content">
          <VMessage
            v-if="globalStatusMessage"
            :type="globalStatusMessage.type"
            :title="globalStatusMessage.title"
          />
          <VScrollPanel height="20rem">
            <div class="file-area">
              <div v-if="slotProps.files.length > 0" class="file-non-uploaded-area">
                <VFile
                  v-for="(file, index) in slotProps.files"
                  :key="file.name + file.type + file.size"
                  :file="file"
                  :index="index"
                  :remove-file-callback="slotProps.removeFileCallback"
                  :progress="fileProgressMap[file.name] || 0"
                  status="pending"
                  advanced
                />
              </div>
              <div v-if="slotProps.uploadedFiles.length > 0" class="file-uploaded-area">
                <VFile
                  v-for="(file, index) in slotProps.uploadedFiles"
                  :key="file.name + file.type + file.size"
                  :file="file"
                  :index="index"
                  :remove-file-callback="slotProps.removeFileCallback"
                  :progress="100"
                  status="completed"
                  advanced
                />
              </div>
            </div>
          </VScrollPanel>
        </div>
        <div v-if="!props.advanced && !hasActiveError">
          <VFile
            v-if="lastSelectedFile"
            :file="lastSelectedFile"
            :remove-file-callback="slotProps.removeFileCallback"
            status="pending"
          />
          <VFile
            v-if="slotProps.uploadedFiles.length > 0 && slotProps.uploadedFiles.at(-1)"
            :file="slotProps.uploadedFiles.at(-1)!"
            :remove-file-callback="slotProps.removeFileCallback"
            status="completed"
          />
        </div>
        <div
          role="alert"
          aria-live="polite"
        >
          <VHint
            v-for="message of slotProps.messages"
            :key="message"
            :title="message"
            type="alert"
            icon
          />
        </div>
        <VLabelErrorProxy :hasError="(slotProps.messages ?? []).length > 0" @update:error="hasActiveError = $event" />
      </template>
    </FileUpload>
  </div>
</template>

<style lang="css" scoped>
*{
  margin: 0px;
  padding: 0px;
}

.container {
  width: 100%;
  height: auto;
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 0.5rem;
}

.p-fileupload{
  width: 100%;
  --p-fileupload-border-color: v-bind(borderColor);
  padding: v-bind(padding);
}

.simple{
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--p-fileupload-file-gap);
}

.fileupload-empty{
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 1rem;
  gap: 1.5rem;
}

.upload-cloud-icon{
  font-size: 5rem;
  color: var(--border-contrast-grey);
}

.file-content,
.file-area{
  display: flex;
  flex-direction: column;
}

.file-content{gap: 0.75rem;}
.file-area{gap: 0.5rem;}

.file-non-uploaded-area,
.file-uploaded-area {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 0.25rem;
}
</style>
