'use strict';

var clsx = require('clsx');
var React = require('react');
var reactIntl = require('react-intl');
var Button = require('../button/Button.js');
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');
var jsxRuntime = require('react/jsx-runtime');
var status = require('../common/propsValues/status.js');
var control = require('../common/propsValues/control.js');

function generateFileId(file) {
  const {
    name,
    size
  } = file;
  const uploadTimeStamp = new Date().getTime();
  return `${name}_${size}_${uploadTimeStamp}`;
}
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 [fileToRemoveIndex, setFileToRemoveIndex] = React.useState(null);
  const [mounted, setMounted] = React.useState(false);
  const {
    formatMessage
  } = reactIntl.useIntl();
  const itemRefs = React.useRef([]);
  const uploadInputRef = React.useRef(null);
  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 addFileToList(recentUploadedFile) {
    function addToList(listToAddTo) {
      return [...listToAddTo, recentUploadedFile];
    }
    setUploadedFiles(addToList);
    uploadedFilesListReference.current = addToList(uploadedFilesListReference.current);
  }
  const removeFileFromList = file => {
    function filterOutFrom(listToFilterFrom) {
      return listToFilterFrom.filter(fileInList => file !== fileInList && file.id !== fileInList.id);
    }
    setUploadedFiles(filterOutFrom);
    uploadedFilesListReference.current = filterOutFrom(uploadedFilesListReference.current);
  };
  const modifyFileInList = (file, updates) => {
    const updateListItem = listToUpdate => listToUpdate.map(fileInList => {
      return fileInList === file || fileInList.id === file.id ? {
        ...file,
        ...updates
      } : fileInList;
    });
    setUploadedFiles(updateListItem);
    uploadedFilesListReference.current = updateListItem(uploadedFilesListReference.current);
  };
  const [fileToRemove, setFileToRemove] = React.useState(null);
  const removeFile = file => {
    const {
      id,
      status: status$1
    } = file;
    const index = uploadedFiles.findIndex(f => f.id === file.id);
    setFileToRemoveIndex(index);
    if (status$1 === status.Status.FAILED) {
      removeFileFromList(file);
      setFileToRemove(file);
    } else if (onDeleteFile && id) {
      modifyFileInList(file, {
        status: status.Status.PROCESSING,
        error: undefined
      });
      onDeleteFile(id).then(() => {
        removeFileFromList(file);
      }).catch(error => {
        modifyFileInList(file, {
          error: error
        });
      }).finally(() => {
        setFileToRemove(file);
      });
    }
  };
  function handleFileUploadFailure(file, failureMessage) {
    const {
      name
    } = file;
    const id = generateFileId(file);
    const failedUpload = {
      id,
      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;
  }
  // One or more files selected, create entries for them
  const addFiles = selectedFiles => {
    for (let i = 0; i < selectedFiles.length; i += 1) {
      const file = selectedFiles.item(i);
      // Returning a FormData[] array instead of FileList so we can filter out incorrect files
      const formData = new FormData();
      if (file) {
        generateFileId(file);
        const allowedFileTypes = typeof fileTypes === 'string' ? fileTypes : fileTypes.join(',');
        // Check if file type is valid
        if (!isTypeValid.isTypeValid(file, allowedFileTypes)) {
          handleFileUploadFailure(file, formatMessage(UploadInput_messages.fileTypeNotSupported));
          continue;
        }
        // Check if the filesize is valid
        // Convert to rough bytes
        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;
        }
        // Check if the file is already in the list
        const existingFile = uploadedFiles.find(f => f.filename === file.name);
        if (existingFile) {
          // Remove the file from the list before adding it again
          removeFileFromList(existingFile);
        }
        // Add the file to the list
        formData.append(fileInputName, file);
        const pendingFile = {
          id: generateFileId(file),
          filename: file.name,
          status: status.Status.PENDING
        };
        addFileToList(pendingFile);
        // Start uploading the file
        onUploadFile(formData).then(({
          id,
          url,
          error
        }) => {
          // Replace the temporary id with the final one received from the API, and also set any errors
          modifyFileInList(pendingFile, {
            id,
            url,
            error,
            status: status.Status.SUCCEEDED
          });
        }).catch(error => {
          modifyFileInList(pendingFile, {
            error: error,
            status: status.Status.FAILED
          });
        });
        if (!multiple) {
          // Only upload a single file
          break;
        }
      }
    }
  };
  React.useLayoutEffect(() => {
    if (fileToRemove && fileToRemoveIndex !== null) {
      requestAnimationFrame(() => {
        const nextFocusIndex = Math.min(fileToRemoveIndex, uploadedFiles.length - 1);
        if (itemRefs.current[nextFocusIndex]) {
          itemRefs.current[nextFocusIndex].focus(); // Focus the next UploadItem
        } else {
          // If there's only one item left, focus the UploadButton
          uploadInputRef.current?.focus();
        }
      });
      setFileToRemove(null); // Reset the state
      setFileToRemoveIndex(null); // Reset the index
    }
  }, [uploadedFiles, fileToRemove, fileToRemoveIndex, itemRefs, uploadInputRef]);
  React.useEffect(() => {
    setMounted(true);
  }, []);
  React.useEffect(() => {
    if (onFilesChange && mounted) {
      onFilesChange([...uploadedFiles]);
    }
  }, [onFilesChange, uploadedFiles]); // eslint-disable-line react-hooks/exhaustive-deps
  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 => {
            itemRefs.current[index] = 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 ? () => removeFile(file) : () => setMarkedFileForDelete(file),
          onDownload: onDownload
        }, 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,
          onClick: () => {
            if (markedFileForDelete) {
              removeFile(markedFileForDelete);
            }
            setMarkedFileForDelete(null);
          },
          children: deleteConfirm?.confirmText || formatMessage(UploadInput_messages.deleteModalConfirmButtonText)
        })]
      }),
      onClose: () => {
        setMarkedFileForDelete(null);
      }
    })]
  });
};

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