'use strict';

var clsx = require('clsx');
var React = require('react');
var reactIntl = require('react-intl');
var Button = require('../button/Button.js');
require('../common/theme.js');
require('../common/direction.js');
var control = require('../common/propsValues/control.js');
require('../common/propsValues/breakpoint.js');
require('../common/propsValues/size.js');
require('../common/propsValues/typography.js');
require('../common/propsValues/width.js');
require('../common/propsValues/type.js');
require('../common/propsValues/dateMode.js');
require('../common/propsValues/monthFormat.js');
require('../common/propsValues/position.js');
require('../common/propsValues/layouts.js');
var status = require('../common/propsValues/status.js');
require('../common/propsValues/sentiment.js');
require('../common/propsValues/profileType.js');
require('../common/propsValues/variant.js');
require('../common/propsValues/scroll.js');
require('../common/propsValues/markdownNodeType.js');
require('../common/fileType.js');
require('@transferwise/icons');
require('../common/closeButton/CloseButton.messages.js');
var jsxRuntime = require('react/jsx-runtime');
var contexts = require('../inputs/contexts.js');
var Modal = require('../modal/Modal.js');
var isSizeValid = require('../upload/utils/isSizeValid/isSizeValid.js');
var isTypeValid = require('../upload/utils/isTypeValid/isTypeValid.js');
var UploadInput_messages = require('./UploadInput.messages.js');
var UploadButton = require('./uploadButton/UploadButton.js');
var defaults = require('./uploadButton/defaults.js');
var UploadItem = require('./uploadItem/UploadItem.js');

function generateFileId(file) {
  const {
    name,
    size
  } = file;
  const uploadTimeStamp = new Date().getTime();
  return `${name}_${size}_${uploadTimeStamp}`;
}
/**
 * The component allows users to upload files, manage the list of uploaded files,
 * and handle file validation and deletion.
 *
 * @param {UploadInputProps} props - The properties for the UploadInput component.
 *
 * @see {@link UploadInput} for further information.
 * @see {@link https://storybook.wise.design/?path=/docs/forms-uploadinput--docs|Storybook Wise Design}
 */
const UploadInput = ({
  files = [],
  fileInputName = 'file',
  className,
  deleteConfirm,
  disabled,
  multiple = false,
  fileTypes = defaults.imageFileTypes,
  sizeLimit = defaults.DEFAULT_SIZE_LIMIT,
  description,
  onUploadFile,
  onDeleteFile,
  onValidationError,
  onFilesChange,
  onDownload,
  maxFiles,
  maxFilesErrorMessage,
  id,
  sizeLimitErrorMessage,
  uploadButtonTitle
}) => {
  const inputAttributes = contexts.useInputAttributes({
    nonLabelable: true
  });
  const [markedFileForDelete, setMarkedFileForDelete] = React.useState(null);
  const [mounted, setMounted] = React.useState(false);
  const {
    formatMessage
  } = reactIntl.useIntl();
  const uploadInputRef = React.useRef(null);
  let fileRefs = [];
  const PROGRESS_STATUSES = new Set([status.Status.PENDING, status.Status.PROCESSING]);
  const [uploadedFiles, setUploadedFiles] = React.useState(multiple || files.length === 0 ? files : [files[0]]);
  const uploadedFilesListReference = React.useRef(multiple || files.length === 0 ? files : [files[0]]);
  function updateFileList(updateFn) {
    setUploadedFiles(updateFn);
    uploadedFilesListReference.current = updateFn(uploadedFilesListReference.current);
  }
  function addFileToList(recentUploadedFile) {
    updateFileList(list => [...list, recentUploadedFile]);
  }
  function removeFileFromList(file) {
    updateFileList(list => list.filter(fileInList => file !== fileInList && file.id !== fileInList.id));
    fileRefs = fileRefs.filter(ref => ref && ref.id !== file.id);
  }
  function modifyFileInList(file, updates) {
    updateFileList(list => list.map(fileInList => fileInList === file || fileInList.id === file.id ? {
      ...file,
      ...updates
    } : fileInList));
  }
  const removeFile = async file => {
    const {
      id,
      status: status$1
    } = file;
    fileRefs = fileRefs.filter(item => item && item.id !== file.id);
    if (status$1 === status.Status.FAILED) {
      removeFileFromList(file);
      return Promise.resolve();
    }
    if (onDeleteFile && id) {
      modifyFileInList(file, {
        status: status.Status.PROCESSING,
        error: undefined
      });
      return onDeleteFile(id).then(() => {
        removeFileFromList(file);
      }).catch(error => {
        modifyFileInList(file, {
          error: error
        });
      });
    }
  };
  function handleFileUploadFailure(file, failureMessage) {
    const {
      name
    } = file;
    const failedUpload = {
      id: generateFileId(file),
      filename: name,
      status: status.Status.FAILED,
      error: failureMessage
    };
    addFileToList(failedUpload);
    if (onValidationError) {
      onValidationError(failedUpload);
    }
  }
  function getNumberOfFilesUploaded() {
    const uploadInitiatedStatus = new Set([status.Status.SUCCEEDED, status.Status.PENDING]);
    const validFiles = uploadedFilesListReference.current.filter(file => file.status && uploadInitiatedStatus.has(file.status));
    return validFiles.length;
  }
  function areMaximumFilesUploadedAlready() {
    if (!maxFiles) {
      return false;
    }
    const numberOfValidFiles = getNumberOfFilesUploaded();
    return numberOfValidFiles >= maxFiles;
  }
  const addFiles = selectedFiles => {
    for (let i = 0; i < selectedFiles.length; i += 1) {
      const file = selectedFiles.item(i);
      const formData = new FormData();
      if (file) {
        const allowedFileTypes = typeof fileTypes === 'string' ? fileTypes : fileTypes.join(',');
        if (!isTypeValid.isTypeValid(file, allowedFileTypes)) {
          handleFileUploadFailure(file, formatMessage(UploadInput_messages.fileTypeNotSupported));
          continue;
        }
        if (!isSizeValid.isSizeValid(file, sizeLimit * 1000)) {
          const failureMessage = sizeLimitErrorMessage || formatMessage(UploadInput_messages.fileIsTooLarge);
          handleFileUploadFailure(file, failureMessage);
          continue;
        }
        if (areMaximumFilesUploadedAlready()) {
          const failureMessage = maxFilesErrorMessage || formatMessage(UploadInput_messages.maximumFilesAlreadyUploaded, {
            maxFilesAllowed: maxFiles
          });
          handleFileUploadFailure(file, failureMessage);
          continue;
        }
        const existingFile = uploadedFiles.find(f => f.filename === file.name);
        if (existingFile) {
          removeFileFromList(existingFile);
        }
        formData.append(fileInputName, file);
        const pendingFile = {
          id: generateFileId(file),
          filename: file.name,
          status: status.Status.PENDING
        };
        addFileToList(pendingFile);
        onUploadFile(formData).then(({
          id,
          url,
          error
        }) => {
          modifyFileInList(pendingFile, {
            id,
            url,
            error,
            status: status.Status.SUCCEEDED
          });
        }).catch(error => {
          modifyFileInList(pendingFile, {
            error: error,
            status: status.Status.FAILED
          });
        });
        if (!multiple) {
          break;
        }
      }
    }
  };
  React.useEffect(() => {
    setMounted(true);
  }, []);
  React.useEffect(() => {
    if (onFilesChange && mounted) {
      onFilesChange([...uploadedFiles]);
    }
  }, [onFilesChange, uploadedFiles]); // eslint-disable-line react-hooks/exhaustive-deps
  const [nextFocusable, setNextFocusable] = React.useState(uploadInputRef.current);
  const handleFocus = fileId => {
    fileRefs = fileRefs.filter(ref => {
      return ref && ref.id !== markedFileForDelete?.id;
    });
    const filesCount = fileRefs.length;
    let next = uploadInputRef.current;
    if (filesCount > 1) {
      const currentFileIndex = fileRefs.findIndex(file => file?.id === fileId);
      const currentFileId = fileRefs?.[currentFileIndex]?.id;
      const lastFileId = fileRefs?.[filesCount - 1]?.id;
      // if last file, select a previous one
      if (currentFileId === lastFileId) {
        next = fileRefs[filesCount - 2];
      } else {
        next = fileRefs[currentFileIndex + 1];
      }
    }
    setNextFocusable(next);
  };
  const handleRefocus = () => {
    if (nextFocusable && 'focus' in nextFocusable && typeof nextFocusable.focus === 'function') {
      setTimeout(() => {
        nextFocusable.focus();
      }, 0);
    }
  };
  return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
    children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
      role: "group",
      className: clsx.clsx('np-upload-input', className, {
        disabled
      }),
      ...inputAttributes,
      children: [/*#__PURE__*/jsxRuntime.jsx("div", {
        className: "np-upload-input__section",
        "aria-live": "polite",
        "aria-relevant": "all",
        role: "region",
        children: uploadedFiles.map((file, index) => /*#__PURE__*/jsxRuntime.jsx(UploadItem.default, {
          ref: el => {
            if (el && el.id !== markedFileForDelete?.id && !fileRefs.some(ref => ref && ref.id === el.id) && el.status !== 'processing') {
              fileRefs.push(el);
            }
          },
          file: file,
          singleFileUpload: !multiple,
          canDelete: (!!onDeleteFile || file.status === status.Status.FAILED) && (!file.status || !PROGRESS_STATUSES.has(file.status)),
          onDelete: file.status === status.Status.FAILED ? async () => {
            await removeFile(file);
            handleRefocus();
          } : () => setMarkedFileForDelete(file),
          onDownload: onDownload,
          onFocus: () => handleFocus(file.id)
        }, file.id))
      }), (multiple || !multiple && !uploadedFiles.length) && /*#__PURE__*/jsxRuntime.jsx("div", {
        className: "np-upload-input__section np-upload-input__section--uploader",
        children: /*#__PURE__*/jsxRuntime.jsx(UploadButton.default, {
          ref: uploadInputRef,
          id: id,
          uploadButtonTitle: uploadButtonTitle,
          disabled: areMaximumFilesUploadedAlready() || disabled,
          multiple: multiple,
          fileTypes: fileTypes,
          sizeLimit: sizeLimit,
          description: description,
          maxFiles: maxFiles,
          withEntries: Boolean(uploadedFiles.length),
          onChange: addFiles
        })
      })]
    }), /*#__PURE__*/jsxRuntime.jsx(Modal, {
      title: deleteConfirm?.title !== undefined ? deleteConfirm.title : formatMessage(UploadInput_messages.deleteModalTitle),
      body: deleteConfirm?.body !== undefined ? deleteConfirm.body : formatMessage(UploadInput_messages.deleteModalBody),
      open: !!markedFileForDelete,
      footer: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
        children: [/*#__PURE__*/jsxRuntime.jsx(Button, {
          block: true,
          onClick: () => {
            setMarkedFileForDelete(null);
          },
          children: deleteConfirm?.cancelText || formatMessage(UploadInput_messages.deleteModalCancelButtonText)
        }), /*#__PURE__*/jsxRuntime.jsx(Button, {
          block: true,
          priority: control.Priority.SECONDARY,
          type: control.ControlType.NEGATIVE,
          tabIndex: markedFileForDelete ? 0 : -1,
          onClick: () => {
            if (markedFileForDelete) {
              void removeFile(markedFileForDelete);
            }
            setMarkedFileForDelete(null);
          },
          children: deleteConfirm?.confirmText || formatMessage(UploadInput_messages.deleteModalConfirmButtonText)
        })]
      }),
      onUnmount: handleRefocus,
      onClose: () => {
        setMarkedFileForDelete(null);
      }
    })]
  });
};

module.exports = UploadInput;
//# sourceMappingURL=UploadInput.js.map
