import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useConsolidatedRef, useI18n, useUID, useTestIds, usePrevious, useElement, useModalManager, useModalContext, useLiveLog } from '../../hooks';
import Flex from '../Flex';
import Text from '../Text';
import Icon, { registerIcon } from '../Icon';
import * as paperClipIcon from '../Icon/icons/paper-clip.icon';
import { getActiveElement, getFocusables, withTestIds, createClassName } from '../../utils';
import Tooltip from '../Tooltip';
import Button from '../Button';
import Modal, {} from '../Modal';
import VisuallyHiddenText from '../VisuallyHiddenText';
import MetaList from '../MetaList';
import { getFileInputTestIds } from './File.test-ids';
import FileList from './FileList';
import FileItem from './FileItem';
import { StyledBareButton, StyledDropZone, StyledFileInput, StyledIconWrapper, StyledAttachedFileCount, StyledSingleFileWrapper, StyledUploadStatus, StyledFlex, StyledFormField, StyledFileInputControl, StyledDownloadAllButton } from './FileInput.styles';
registerIcon(paperClipIcon);
const Component = ({ compact, multiple, status, count, dragOver = false, readOnly }) => {
    const t = useI18n();
    const attachedFileCount = multiple && count ? (_jsx(StyledAttachedFileCount, { children: t('number_of_items', [count], { count }) })) : undefined;
    return (_jsxs(_Fragment, { children: [_jsx(StyledUploadStatus, { item: { grow: 1 }, container: { justify: compact && multiple ? 'start' : 'center', alignItems: 'center' }, status: status, dragOver: dragOver, readOnly: readOnly, children: _jsx(Text, { variant: 'secondary', children: compact
                        ? attachedFileCount
                        : t('file_upload_text_main', [
                            t(multiple ? 'file_upload_text_multiple' : 'file_upload_text_one')
                        ]) }) }), _jsx(StyledIconWrapper, { container: { justify: 'center', alignItems: 'center' }, children: _jsx(Icon, { name: 'paper-clip' }) })] }));
};
const BaseFileInput = forwardRef(function FileInput(props, ref) {
    const uid = useUID();
    const { testId, id = uid, label, labelHidden, info, required = false, disabled = false, readOnly = false, files, onDownloadAll, onFilesAdded, multiple = false, status, additionalInfo, accept, compact = false, previewFromActions = false, className, ...restProps } = props;
    const testIds = useTestIds(testId, getFileInputTestIds);
    const [target, setTarget] = useElement();
    const inputRef = useConsolidatedRef(ref);
    const fileItemListRef = useRef();
    const activeElement = getActiveElement();
    const activeElementInList = fileItemListRef.current?.contains(activeElement);
    const isInputActiveElement = activeElement === inputRef.current;
    const compactWithSingleAttach = compact && !multiple;
    const filesAnnounced = useRef(false);
    const announcedFilesSet = useRef(new Set());
    const { announcePolite } = useLiveLog();
    const onChange = useCallback((e) => {
        if (e.target.files) {
            onFilesAdded?.(Array.from(e.target.files));
        }
        /**
         * WHY are we doing this?
         * We are not working with the "value" prop of the input but rather the File instances that are created.
         * The consumer of this component should manage an array or File(s) and allow for a user to remove a selected file.
         * Since we can not modify the input value of files for browser security reasons,
         * and since setting the files property is less than ideal, we reset/toggle the input to a fresh state after a render.
         * This enables onChange to fire even if the user had just previously selected a file, removed it, and selected it again.
         */
        if (inputRef.current)
            inputRef.current.value = '';
    }, [onFilesAdded]);
    const t = useI18n();
    const [dragOver, setDragOver] = useState(false);
    const hideFileInput = !multiple && files && files.length > 0;
    const fileInput = (_jsxs(StyledFileInput, { children: [_jsx("input", { "data-testid": testIds.control, ...restProps, ref: inputRef, type: 'file', id: id, required: required, disabled: disabled, readOnly: readOnly, className: createClassName('file-input', className, {
                    compact
                }), onChange: onChange, onFocus: () => {
                    if (target) {
                        target.dispatchEvent(new Event('focusin'));
                    }
                }, onBlur: () => {
                    if (target) {
                        target.dispatchEvent(new Event('focusout'));
                    }
                }, multiple: multiple, accept: Array.isArray(accept) ? accept.join(',') : accept, "aria-describedby": `${id}-info`, onClick: readOnly
                    ? e => {
                        e.preventDefault();
                    }
                    : undefined }), _jsx(Flex, { container: true, as: StyledDropZone, disabled: disabled, readOnly: readOnly, onClick: () => {
                    inputRef.current?.focus();
                    if (!readOnly) {
                        inputRef.current?.click();
                    }
                }, ...(!disabled && !readOnly
                    ? {
                        onDragOver: (e) => {
                            e.preventDefault();
                        },
                        onDragEnter: () => setDragOver(true),
                        onDragLeave: () => setDragOver(false),
                        onDrop: (e) => {
                            e.preventDefault();
                            setDragOver(false);
                            const fileList = Array.from(e.dataTransfer.files);
                            onFilesAdded?.(multiple ? fileList : fileList.slice(0, 1));
                        }
                    }
                    : undefined), compactMode: compact, ref: setTarget, children: _jsx(Component, { compact: compact, multiple: multiple, status: status, dragOver: dragOver, readOnly: readOnly }) }), compactWithSingleAttach && target && (_jsx(Tooltip, { target: target, describeTarget: true, children: t('file_upload_text_main', [t('file_upload_text_one')]) }))] }));
    const getReadOnlyFile = (file) => {
        const readOnlyFile = { ...file };
        delete readOnlyFile.onCancel;
        delete readOnlyFile.onEdit;
        delete readOnlyFile.onDelete;
        delete readOnlyFile.progress;
        return readOnlyFile;
    };
    const fileItemList = hideFileInput ? (_jsx(StyledSingleFileWrapper, { "data-testid": testIds.files, compact: compact, ref: element => {
            if (element)
                fileItemListRef.current = element;
        }, children: _jsx(FileItem, { ...(readOnly ? getReadOnlyFile(files[0]) : files[0]), compact: compact, previewFromActions: compactWithSingleAttach && previewFromActions, disabled: disabled }) })) : (_jsx(FileList, { type: 'item', items: readOnly
            ? files?.map(file => {
                return getReadOnlyFile(file);
            }) ?? []
            : files ?? [], "data-testid": testIds.files, disabled: disabled, ref: element => {
            if (element)
                fileItemListRef.current = element;
        } }));
    const prevFiles = usePrevious(files);
    useEffect(() => {
        if (!multiple &&
            prevFiles?.length === 0 &&
            files?.length === 1 &&
            fileItemListRef.current &&
            isInputActiveElement) {
            getFocusables(fileItemListRef.current)[0]?.focus();
        }
        if (prevFiles?.length === 1 && files?.length === 0 && activeElementInList) {
            inputRef.current?.focus();
        }
        if (!files)
            return;
        if (announcedFilesSet.current.size !== files.length) {
            filesAnnounced.current = false;
        }
        if ((prevFiles?.length ?? 0) > files.length) {
            announcedFilesSet.current = new Set(Array.from(announcedFilesSet.current).filter(item => files.some(file => file.id === item)));
        }
        const errorFiles = files.filter(item => !announcedFilesSet.current.has(item.id) && item.error);
        const progressFiles = files.filter(item => !announcedFilesSet.current.has(item.id) &&
            ('progress' in item ? typeof item.progress === 'number' && item.progress > 99 : true));
        const totalAttachedFilesLength = progressFiles.length + errorFiles.length;
        if (totalAttachedFilesLength > 0 &&
            files.length - announcedFilesSet.current.size === totalAttachedFilesLength &&
            !filesAnnounced.current) {
            if (progressFiles.length > 0) {
                announcePolite({
                    message: t('files_uploaded', [progressFiles.length], { count: progressFiles.length })
                });
                progressFiles.forEach(file => {
                    if (file.id && !announcedFilesSet.current.has(file.id)) {
                        announcedFilesSet.current.add(file.id);
                    }
                });
            }
            if (errorFiles.length > 0) {
                announcePolite({
                    message: t('files_upload_failed', [errorFiles.length], { count: errorFiles.length })
                });
                errorFiles.forEach(file => {
                    if (file.id && !announcedFilesSet.current.has(file.id)) {
                        announcedFilesSet.current.add(file.id);
                    }
                });
            }
            filesAnnounced.current = true;
        }
    }, [files]);
    const renderElement = hideFileInput ? fileItemList : fileInput;
    const fileInputInfoActions = useMemo(() => {
        if (info && !onDownloadAll) {
            return info;
        }
        const downloadAllButton = (_jsx(Button, { onClick: onDownloadAll, variant: 'link', children: t('download_all') }));
        if (onDownloadAll && !info && files && files.length > 1 && multiple) {
            return downloadAllButton;
        }
        if (info && onDownloadAll) {
            if (files && files.length > 1 && multiple) {
                return _jsx(MetaList, { items: [info, downloadAllButton] });
            }
            return info;
        }
    }, [info, onDownloadAll]);
    return (_jsxs(Flex, { container: { rowGap: hideFileInput ? undefined : 1, direction: 'column' }, as: StyledFileInputControl, status: status, children: [label ? (_jsx(StyledFormField, { testId: testIds, label: _jsxs(_Fragment, { children: [label, readOnly && _jsx(VisuallyHiddenText, { children: t('read_only') })] }), labelHidden: labelHidden, id: id, info: fileInputInfoActions, required: required, disabled: !hideFileInput && disabled, readOnly: readOnly, status: status, additionalInfo: additionalInfo, compactMode: compactWithSingleAttach, children: _jsxs(_Fragment, { children: [renderElement, readOnly && files && files.length > 1 && multiple && (_jsx(StyledDownloadAllButton, { variant: 'link', onClick: onDownloadAll, children: t('download_all') }))] }) })) : (renderElement), multiple && !compact && files && files.length > 0 && fileItemList] }));
});
const AttachFilesModal = ({ modalContent, modal, readOnly }) => {
    const t = useI18n();
    const { dismiss } = useModalContext();
    const formActions = (_jsxs(_Fragment, { children: [_jsx(Button, { disabled: modal?.loading, onClick: () => {
                    if (modal?.onCancel)
                        modal.onCancel({ close: dismiss });
                }, children: t('cancel') }), _jsx(Button, { disabled: modal?.loading, type: 'submit', variant: 'primary', onClick: () => {
                    if (modal?.onSubmit)
                        modal.onSubmit({ close: dismiss });
                }, children: t('submit') })] }));
    return (_jsx(Modal, { progress: modal?.loading, heading: t('attach_files'), actions: !readOnly ? formActions : undefined, onBeforeOpen: modal?.onBeforeOpen, children: modalContent }));
};
const FileInput = forwardRef(function FileInput(props, ref) {
    const uid = useUID();
    const { modal, count, ...baseFileInputProps } = props;
    const { id = uid, label, labelHidden, info, required = false, disabled = false, readOnly = false, onFilesAdded, multiple = false, status, additionalInfo, compact = false } = baseFileInputProps;
    const t = useI18n();
    const [dragOver, setDragOver] = useState(false);
    const [buttonEl, setButtonEl] = useElement();
    const { create } = useModalManager();
    const modalMethods = useRef();
    let modalContent;
    if (compact && multiple) {
        modalContent = (_jsx(BaseFileInput, { ...baseFileInputProps, multiple: true, compact: false, labelHidden: false, ref: ref }));
    }
    const modalProps = {
        modal,
        modalContent
    };
    useEffect(() => {
        if (compact && multiple && modal) {
            modalMethods.current?.update({
                modal,
                modalContent,
                readOnly
            });
        }
    }, [compact, multiple, modal, readOnly, modalProps]);
    useEffect(() => {
        return () => {
            if (modalMethods.current) {
                modalMethods.current?.dismiss();
                modalMethods.current?.unmount();
            }
        };
    }, []);
    if (compact && multiple) {
        const render = (_jsxs(Flex, { container: true, children: [_jsx(StyledBareButton, { id: id, onClick: () => {
                        modalMethods.current = create(AttachFilesModal, { ...modalProps, readOnly }, { dismissible: readOnly });
                    }, "aria-label": count && count > 0
                        ? `${t('attach_review')} ${t('number_of_items', [count], { count })}`
                        : undefined, "aria-describedby": `${id}-info`, disabled: disabled, ...(!disabled && !readOnly
                        ? {
                            onDragOver: (e) => {
                                e.preventDefault();
                            },
                            onDragEnter: () => setDragOver(true),
                            onDragLeave: () => setDragOver(false),
                            onDrop: (e) => {
                                e.preventDefault();
                                setDragOver(false);
                                onFilesAdded?.(Array.from(e.dataTransfer.files));
                                if (buttonEl)
                                    buttonEl.click();
                            }
                        }
                        : undefined), ref: setButtonEl, children: _jsx(StyledFlex, { container: true, children: _jsx(Component, { compact: compact, multiple: multiple, status: status, count: count, dragOver: dragOver, readOnly: readOnly }) }) }), buttonEl && (_jsx(Tooltip, { target: buttonEl, describeTarget: count === 0, children: count && count > 0
                        ? t('attach_review')
                        : t('file_upload_text_main', [t('file_upload_text_multiple')]) }))] }));
        return label ? (_jsx(StyledFormField, { label: label, labelHidden: labelHidden, id: id, info: info, required: required, disabled: disabled, readOnly: readOnly, status: status, additionalInfo: additionalInfo, compactMode: true, children: render })) : (render);
    }
    return _jsx(BaseFileInput, { ...baseFileInputProps, ref: ref });
});
export default withTestIds(FileInput, getFileInputTestIds);
//# sourceMappingURL=FileInput.js.map