<template>
  <div class="mc-fileuploader">
    <input
      :id="id"
      ref="inputRef"
      type="file"
      class="mc-fileuploader__input"
      :accept="accept"
      :disabled="disabled"
      :multiple="multiple"
      @change="onChange"
      @cancel="onCancel"
    />
    <label :for="id" class="mc-fileuploader__label">
      <span class="mc-fileuploader__label--center">
        {{ label }}
      </span>
    </label>
    <MFileResult
      v-if="displayFilesList && localFiles?.length > 0"
      :files="localFiles"
      :allowed-extensions="allowedExtensions"
      :max-size="maxSize"
      :remove-label="removeLabel"
      @file-removed="deleteFile"
      @has-invalid-files="hasInvalidFiles"
    >
      <template v-for="(index, name) in $slots" #[name]>
        <!-- @slot Use this slot to format the way the file name is displayed in the list of loaded files -->
        <slot :name="name" />
      </template>
    </MFileResult>
    <MFileResult
      v-if="uploadedFiles.length > 0"
      :files="uploadedFiles"
      :allowed-extensions="allowedExtensions"
      :remove-label="removeLabel"
      :disabled="disabled"
      @file-removed="deleteRemoteFile"
      @has-invalid-files="hasInvalidUploadedFiles"
    >
      <template v-for="(index, name) in $slots" #[name]>
        <!-- @slot Use this slot to format the way the file name is displayed in the list of loaded files -->
        <slot :name="name" />
      </template>
    </MFileResult>
  </div>
</template>

<script>
import MFileResult from './MFileResult.vue';

/**
 * > A file uploader is a pattern mainly used in forms. It allows the user to upload one or several files like pictures or documents.
 *
 * The `MFileUploader` component is the **Vue.js** implementation of the **File uploader** component of Mozaic Design System.<br/>
 * The full specification of this component is available in [the associated documentation](https://mozaic.adeo.cloud/components/form/fileUploader/).
 */
export default {
  name: 'MFileUploader',
  components: {
    MFileResult,
  },
  inheritAttrs: false,
  props: {
    /**
     * The value of files
     */
    files: {
      type: Array,
      default: () => [],
    },
    /**
     * The value of the `id` attribute of the input element
     */
    id: {
      type: String,
      default: null,
      required: true,
    },
    /**
     * The component label
     */
    label: {
      type: String,
      required: true,
    },
    /**
     * The value of the HTML `accept` attribute. Takes as its value a comma-separated list describing the file types to be allowed. See [HTML attribute: accept](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept)
     * @type {"image/png, image/jpeg"}
     */
    accept: {
      type: String,
      default: null,
    },
    /**
     * The value of the HTML `multiple` attribute. Allows the user to select more than one file. See [HTML attribute: multiple](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/multiple)
     */
    multiple: {
      type: Boolean,
      default: false,
    },
    /**
     * Array taking as value a list of allowed file extensions to consider the field as valid
     * @type {['png', 'jpg', 'pdf']}
     */
    allowedExtensions: {
      type: Array,
      default: () => [],
    },
    /**
     * File size in Megabytes not to be exceeded
     */
    maxSize: {
      type: Number,
      default: null,
    },
    /**
     * Displays the list of loaded file(s).
     */
    displayFilesList: {
      type: Boolean,
      default: true,
    },
    /**
     * `Array` or `FileList` of previously uploaded files _(from a server, for example)_ that you can pass to the component
     */
    uploadedFiles: {
      type: [Array, Object],
      default: function () {
        return [];
      },
    },
    /**
     * Allows to disabled the component
     */
    disabled: {
      type: Boolean,
      default: false,
    },
    /**
     * Label of the delete button for downloaded files. _(Useful for improving the accessibility of the component)_
     */
    removeLabel: {
      type: String,
      default: 'Remove',
    },
  },
  emits: [
    'file-added',
    'file-removed',
    'remote-file-removed',
    'has-invalid-files',
    'on-cancel',
    'update:files',
  ],
  data() {
    return {
      localFiles: [...this.files],
      invalidFiles: [],
      invalidUploadedFiles: [],
    };
  },
  watch: {
    files: {
      handler(newFiles) {
        this.localFiles = Array.isArray(newFiles) ? [...newFiles] : [];
      },
      deep: true,
    },
  },
  methods: {
    onCancel(e) {
      this.$emit('on-cancel', e);
    },
    onChange(e) {
      this.localFiles = [...e.target.files];
      /**
       * Triggered each time a file is loaded inside the component.
       * - @event **file-added**
       * - @arg {object} - a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object provides information about the loaded file
       */
      this.$emit('file-added', this.localFiles);
      this.$emit('update:files', this.localFiles);
      e.target.value = '';
    },
    removeFromArray(fileList, value) {
      const array = Array.from(fileList);
      const idx = array.indexOf(value);
      if (idx !== -1) {
        array.splice(idx, 1);
      }
      return array;
    },
    deleteFile(file) {
      this.localFiles = this.removeFromArray(this.localFiles, file);
      /**
       * Triggered each time a file is deleted from the list of loaded files
       * - @event **file-removed**
       * - @arg {object} - a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object provides information about the deleted file
       */
      this.$emit('file-removed', file);
      this.$emit('update:files', this.localFiles);
    },
    deleteRemoteFile(file) {
      /**
       * Triggered each time a file is deleted from the list of remote/stateless loaded files
       * - @event **remote-file-removed**
       * - @arg {object} - a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object provides information about the deleted file
       */
      this.$emit('remote-file-removed', file);
    },
    hasInvalidFiles(payload) {
      /**
       * Triggered each time an invalid file is loaded
       * - @event **has-invalid-files**
       * - @arg {object} - { errorType: [TYPE OF ERROR], fileName: "[INVALID-FILENAME.EXTENSION]", fileSize: [FILE SIZE], index: [index of the invalid file in the list of loaded files] }
       */
      this.invalidFiles = payload;
      this.$emit('has-invalid-files', [
        ...this.invalidFiles,
        ...this.invalidUploadedFiles,
      ]);
    },
    hasInvalidUploadedFiles(payload) {
      /**
       * Triggered each time an invalid file is loaded
       * - @event **has-invalid-files**
       * - @arg {object} - { errorType: [TYPE OF ERROR], fileName: "[INVALID-FILENAME.EXTENSION]", fileSize: [FILE SIZE], index: [index of the invalid file in the list of loaded files] }
       */
      this.invalidUploadedFiles = payload;
      this.$emit('has-invalid-files', [
        ...this.invalidFiles,
        ...this.invalidUploadedFiles,
      ]);
    },
    reset() {
      this.$emit('file-removed', [...this.localFiles]);
      this.localFiles = [];
      this.$emit('update:files', []);
      this.$refs.inputRef.value = '';
    },
  },
};
</script>

<style scoped lang="scss">
@import 'settings-tools/all-settings';
@import 'components/c.file-uploader';
</style>
