import React, { useState, useEffect } from 'react';
import classNames from 'classnames';
import uniqueId from 'lodash-es/uniqueId';
import { faTrashCan } from '@fortawesome/free-solid-svg-icons/faTrashCan';
import { faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons/faCloudUploadAlt';
import { faFileLines } from '@fortawesome/free-solid-svg-icons/faFileLines';
import { faFolder } from '@fortawesome/free-solid-svg-icons/faFolder';
import { FontAwesomeIcon } from '../font-awesome-icon';
import { supportsFolderUpload } from './file-uploader-helpers';
import { usePrevious } from '../hooks/use-previous';
import UploadFileStatusMessage from './file-uploader-status-message';
import { ERROR_TYPES, useFileUploaderErrors } from './file-uploader-constants';
import { useFileUploader, findByName } from './file-uploader-hook';
import { DragNDropWrapper } from '../drag-and-drop-wrapper';
import { TruncateString } from '../truncate-string';
import { shouldSubmitViaKeyDown } from '../hooks';
import { Button, ACCENT_TYPES } from '../button';
import { DropdownMenu } from '../dropdown-menu';
import { ListCard } from '../list-card';
import { Message, MESSAGE_TYPES } from '../message';
import { DROPDOWN_PLACEMENT_TYPES } from '../dropdown';
import { useTranslation } from '../hooks/use-translation';
import { ELLIPSIS_PLACE_TYPES } from '../truncate-string/truncate-string';
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
const isFolderUploadSupported = supportsFolderUpload();
/** A unified file uploader component.
 * @midnight-gray-supported
 * @alpine-light-supported
 * */
export function FileUploader({
  serviceHandler,
  children,
  onFileDropdownClose,
  onFileDropdownOpen,
  onFileUploadSuccess = () => {},
  onFileUploadFailure = () => {},
  acceptedFilesList = [],
  isMultiple = false,
  isFolderUpload = false,
  initialFiles = [],
  onFileUpload = () => {},
  onFileDelete = () => {},
  onFileCancel = () => {},
  isExpandedSingleFileUpload = false,
  showAcceptedFilesList = false,
  dragAndDropContainerClassName = '',
  inputTestId = 'file-uploader-input',
  dropdownMenuOptions = [],
  progressBarFiles = [],
  isDisabled = false,
  disableDragNDrop = false,
  showFileList = true,
  placeholderLabel: placeholderLabelParams,
  controlsContent = null,
  isCancelAllowed = true,
  errorMessages: errorMessagesParams = {},
  deleteLabelText: deleteLabelTextParams,
  cancelLabelText: cancelLabelTextParams,
  chooseFileLabelText: chooseFileLabelTextParams,
  acceptedFormatsLabelText: acceptedFormatsLabelTextParams,
  isRequired = false,
  isTouched = false,
  label = '',
  helperText = ''
}) {
  const {
    t
  } = useTranslation();
  const errors = useFileUploaderErrors();
  const errorMessages = {
    ...errors,
    ...errorMessagesParams
  };
  const placeholderLabel = placeholderLabelParams ?? t('Drop file(s) here to upload or');
  const deleteLabelText = deleteLabelTextParams ?? t('Delete');
  const cancelLabelText = cancelLabelTextParams ?? t('Cancel');
  const chooseFileLabelText = chooseFileLabelTextParams ?? t('Choose file');
  const acceptedFormatsLabelText = acceptedFormatsLabelTextParams ?? t('Accepted formats: {{formats}}', {
    formats: acceptedFilesList.join(', ')
  });
  const [isHidden, setIsHidden] = useState(!isMultiple && !isExpandedSingleFileUpload && initialFiles.length === 1);
  const [errorMessage, setErrorMessage] = useState('');
  const [fileInputKey, setFileInputKey] = useState(uniqueId());
  const [dropdownMenuKey, setDropdownMenuKey] = useState(uniqueId());
  const showExpandedSingleFileUpload = isExpandedSingleFileUpload && !isMultiple;
  const {
    addedFileStructure,
    collectedFileStructure,
    generateFileStructure,
    removeFileFromStructure,
    updateFileStructure
  } = useFileUploader({
    isMultiple,
    initialFiles: [],
    setErrorMessage
  });
  const inputRef = React.useRef(null);
  const collectedFileStructureLength = collectedFileStructure.length;
  useEffect(() => {
    if (!!inputRef.current && isFolderUploadSupported && isFolderUpload) {
      inputRef.current.setAttribute('webkitdirectory', isFolderUpload.toString());
    }
  }, [inputRef]);
  const removeFile = file => {
    const {
      name
    } = file;
    if (collectedFileStructureLength <= 1) {
      setIsHidden(false);
    }
    removeFileFromStructure(name);
    setFileInputKey(uniqueId());
  };
  const filesValidation = files => {
    if (!acceptedFilesList.length) {
      return files;
    }
    return files.filter(file => {
      if (!file?.skipTypeValidation) {
        const fileExtensionByName = file.name.match(/\.[0-9a-z]+$/i);
        const {
          FILE_TYPE_IS_NOT_ALLOWED
        } = ERROR_TYPES;
        if (!fileExtensionByName || !acceptedFilesList.some(extension => fileExtensionByName[0].toLowerCase() === extension)) {
          updateFileStructure(file.name, {
            completed: true,
            error: errorMessages[FILE_TYPE_IS_NOT_ALLOWED]
          });
          onFileUploadFailure({
            error: FILE_TYPE_IS_NOT_ALLOWED
          });
          return false;
        }
      }
      return true;
    });
  };
  const onHandleFileUploadSuccess = (data, file) => {
    const name = file?.name ?? '';
    updateFileStructure(name, {
      completed: true
    });
    onFileUploadSuccess(data);
  };
  const onHandleFileUploadFailure = (data, file) => {
    const {
      name
    } = file;
    const {
      error
    } = data;
    const message = error || errorMessages[ERROR_TYPES.DEFAULT_UPLOAD_ERROR];
    updateFileStructure(name, {
      completed: true,
      error: message
    });
    onFileUploadFailure(data);
  };
  const uploadFiles = files => {
    if (serviceHandler) {
      files.forEach(file => {
        serviceHandler(file).then(data => onHandleFileUploadSuccess(data, file)).catch(error => onHandleFileUploadFailure(error, file));
      });
    }
  };
  const handleFileUpload = files => {
    setErrorMessage('');
    setDropdownMenuKey(uniqueId());
    if (showExpandedSingleFileUpload) {
      setIsHidden(false);
    } else if (!isMultiple) {
      setIsHidden(true);
    }
    const validatedFiles = filesValidation(files);
    if (!!validatedFiles.length) {
      uploadFiles(validatedFiles);
      onFileUpload(validatedFiles);
    }
  };
  const handleFileDelete = deleteFile => {
    if (collectedFileStructureLength === 1 && isRequired) {
      setErrorMessage(errorMessages[ERROR_TYPES.IS_REQUIRED] ?? '');
    } else {
      setErrorMessage('');
    }
    onFileDelete(deleteFile);
    removeFile(deleteFile);
  };
  const handleFileCancel = cancelFile => {
    if (collectedFileStructureLength === 1 && !collectedFileStructure[0].completed && isRequired) {
      setErrorMessage(errorMessages[ERROR_TYPES.IS_REQUIRED] ?? '');
    }
    onFileCancel(cancelFile);
    removeFile(cancelFile);
  };

  // update file list with initial or new files
  const prevInitialFiles = usePrevious(initialFiles);
  useEffect(() => {
    const newInitialFiles = initialFiles.filter(item => !findByName(prevInitialFiles || [], item.name));
    if (newInitialFiles.length && (isMultiple || !collectedFileStructureLength)) {
      generateFileStructure({
        files: isMultiple ? newInitialFiles : [newInitialFiles[0]]
      });
    } else if (!initialFiles?.length && prevInitialFiles?.length) {
      // clean up after initial files were removed
      prevInitialFiles.forEach(({
        name
      }) => {
        removeFileFromStructure(name);
      });
    }
  }, [initialFiles]);

  // add file to structure
  useEffect(() => {
    if (!!addedFileStructure.length) {
      handleFileUpload(addedFileStructure);
    }
  }, [addedFileStructure]);
  useEffect(() => {
    if (!collectedFileStructureLength && isRequired && isTouched) {
      setErrorMessage(errorMessages[ERROR_TYPES.IS_REQUIRED] ?? '');
    }
  }, [collectedFileStructure, isRequired, isTouched]);
  const handleFileInputChange = event => {
    generateFileStructure(event.target);
  };
  const handleOnDropdownMenuSelect = (option, files) => {
    if (option && option.id) {
      if (!!option.onOptionSelect) {
        option.onOptionSelect();
      } else if (files?.selectedFiles?.length) {
        generateFileStructure({
          files: files.selectedFiles
        });
      }
    }
  };
  const {
    name,
    completed,
    size,
    details,
    error,
    showFileSize
  } = collectedFileStructure[0] || {};
  return /*#__PURE__*/_jsxs("div", {
    className: "file-uploader",
    children: [label && /*#__PURE__*/_jsx("div", {
      className: "file-uploader-label",
      children: label
    }), helperText && /*#__PURE__*/_jsx("div", {
      className: "file-uploader-helper-text",
      children: helperText
    }), /*#__PURE__*/_jsxs(DragNDropWrapper, {
      onDropAction: generateFileStructure,
      containerClassName: classNames('file-uploader-input', dragAndDropContainerClassName, {
        expanded: showExpandedSingleFileUpload,
        'expanded-has-file': showExpandedSingleFileUpload && collectedFileStructureLength === 1,
        hidden: isHidden,
        error: !!errorMessage,
        disabled: isDisabled || disableDragNDrop
      }),
      isDropAllowed: () => !isDisabled && !disableDragNDrop,
      onDragClassName: classNames({
        'file-uploader-input-on-drag': !isDisabled
      }),
      children: [/*#__PURE__*/_jsx("div", {
        className: classNames('file-uploader-drop-section', {
          expanded: showExpandedSingleFileUpload,
          disabled: isDisabled
        }),
        children: showExpandedSingleFileUpload && collectedFileStructureLength === 1 && /*#__PURE__*/_jsxs(_Fragment, {
          children: [/*#__PURE__*/_jsx(TruncateString, {
            text: name,
            truncateAt: ELLIPSIS_PLACE_TYPES.MIDDLE,
            testId: "file-uploader-single-file-label",
            className: "file-uploader-single-file-name-label"
          }), error && /*#__PURE__*/_jsx(Message, {
            testId: "file-uploader-single-file-error-message",
            className: "file-uploader-error",
            type: MESSAGE_TYPES.ERROR,
            children: error
          }) || /*#__PURE__*/_jsx(UploadFileStatusMessage, {
            isExpanded: showExpandedSingleFileUpload,
            completed: completed,
            size: size,
            details: details,
            progressBarFile: findByName(progressBarFiles, name),
            showProgressBar: !!progressBarFiles.length,
            showFileSize: showFileSize
          }), completed && /*#__PURE__*/_jsx(Button, {
            accentType: ACCENT_TYPES.COMMAND,
            className: "file-uploader-single-file-remove-button",
            testId: `file-uploader-delete-button-${name}`,
            "aria-label": deleteLabelText,
            onClick: () => handleFileDelete(collectedFileStructure[0]),
            leftIcon: faTrashCan,
            children: deleteLabelText
          }), !completed && isCancelAllowed && /*#__PURE__*/_jsx(Button, {
            accentType: ACCENT_TYPES.COMMAND,
            className: "single-file-cancel-button",
            testId: `file-uploader-cancel-button-${name}`,
            "aria-label": cancelLabelText,
            onClick: () => handleFileCancel(collectedFileStructure[0]),
            children: cancelLabelText
          })]
        }) || /*#__PURE__*/_jsxs(_Fragment, {
          children: [/*#__PURE__*/_jsx(FontAwesomeIcon, {
            icon: faCloudUploadAlt,
            className: "file-uploader-cloud-icon"
          }), /*#__PURE__*/_jsx("span", {
            className: "file-uploader-description",
            "test-id": "file-uploader-description",
            children: placeholderLabel
          }), dropdownMenuOptions.length && /*#__PURE__*/_jsx(DropdownMenu, {
            triggerClassName: "file-uploader-dropdown-menu-trigger",
            triggerTestId: "file-uploader-dropdown-menu-trigger",
            contentTestId: "file-uploader-dropdown-menu-content",
            options: dropdownMenuOptions,
            onSelect: handleOnDropdownMenuSelect,
            placement: DROPDOWN_PLACEMENT_TYPES.BOTTOM,
            defaultTriggerText: chooseFileLabelText,
            onClose: onFileDropdownClose,
            onOpen: onFileDropdownOpen,
            isDisabled: isDisabled,
            triggerDynamicContent: () => /*#__PURE__*/_jsxs("span", {
              className: "file-uploader-dropdown-menu-trigger-text-container",
              children: [/*#__PURE__*/_jsx(FontAwesomeIcon, {
                icon: faFolder
              }), /*#__PURE__*/_jsx("span", {
                children: chooseFileLabelText
              })]
            })
          }, dropdownMenuKey) || /*#__PURE__*/_jsx(Button, {
            accentType: ACCENT_TYPES.COMMAND,
            isDisabled: isDisabled,
            leftIcon: faFolder,
            className: "file-uploader-choose-file-button",
            onKeyDown: event => {
              if (shouldSubmitViaKeyDown(event) && !isDisabled) {
                event.stopPropagation();
                event.preventDefault();
                inputRef.current?.click?.();
              }
            },
            children: /*#__PURE__*/_jsx("label", {
              className: "browse-file",
              htmlFor: `browseFile-${fileInputKey}`,
              children: chooseFileLabelText
            })
          }), /*#__PURE__*/_jsx("input", {
            ref: inputRef,
            type: "file",
            id: `browseFile-${fileInputKey}`,
            accept: acceptedFilesList.toString(),
            name: "file",
            onChange: handleFileInputChange,
            multiple: isMultiple,
            "test-id": inputTestId,
            disabled: isDisabled,
            "aria-hidden": true
          }, fileInputKey)]
        })
      }), showAcceptedFilesList && collectedFileStructureLength === 0 && !!acceptedFilesList.length && /*#__PURE__*/_jsx("span", {
        className: "file-uploader-accepted-files",
        "test-id": "file-uploader-accepted-files",
        children: acceptedFormatsLabelText
      })]
    }), errorMessage && /*#__PURE__*/_jsx(Message, {
      type: MESSAGE_TYPES.ERROR,
      className: "file-uploader-error",
      testId: "file-uploader-error",
      children: errorMessage
    }), !showExpandedSingleFileUpload && showFileList && collectedFileStructure && /*#__PURE__*/_jsx("div", {
      className: "file-uploader-dropped-files-section",
      children: collectedFileStructure.map(item => /*#__PURE__*/_jsx(ListCard, {
        testId: "file-uploader-list-card",
        isInteractive: false,
        icon: faFileLines,
        isDisabled: isDisabled,
        titleContent: /*#__PURE__*/_jsxs(_Fragment, {
          children: [/*#__PURE__*/_jsx(TruncateString, {
            truncateAt: ELLIPSIS_PLACE_TYPES.MIDDLE,
            text: item.name
          }), !item.completed && isCancelAllowed && /*#__PURE__*/_jsx(Button, {
            isDisabled: isDisabled,
            accentType: ACCENT_TYPES.COMMAND,
            className: "list-card-cancel-button",
            testId: `file-uploader-cancel-button-${item.name}`,
            "aria-label": cancelLabelText,
            onClick: () => handleFileCancel(item),
            children: cancelLabelText
          })]
        }),
        hasErrored: !!item.error,
        errorMessage: item.error,
        showChevron: false,
        ariaLabelLoading: item.ariaLabelLoading,
        detailsContent: /*#__PURE__*/_jsx("div", {
          className: "file-uploader-details-content",
          children: !item.error ? /*#__PURE__*/_jsx(UploadFileStatusMessage, {
            completed: item.completed,
            size: item.size,
            details: item.details,
            progressBarFile: findByName(progressBarFiles, item.name),
            showProgressBar: !!progressBarFiles.length,
            showFileSize: item.showFileSize
          }) : null
        }),
        controlsContent: item.completed && /*#__PURE__*/_jsxs(_Fragment, {
          children: [controlsContent, /*#__PURE__*/_jsx(Button, {
            accentType: ACCENT_TYPES.ROUND_ICON,
            testId: `file-uploader-delete-button-${item.name}`,
            "aria-label": deleteLabelText,
            onClick: () => handleFileDelete(item),
            isDisabled: isDisabled,
            children: /*#__PURE__*/_jsx(FontAwesomeIcon, {
              icon: faTrashCan
            })
          })]
        })
      }, item.name))
    }), typeof children === 'function' && children({
      handleFilesSelected: generateFileStructure
    })]
  });
}