import { declensionOfWords } from '@astral/utils/declension/declensionOfWords';
import { formatFileSizeToView } from '@astral/utils/file/formatFileSizeToView';
import { useMemo, useState } from 'react';
import { useGenerateLocalId } from '../../../useGenerateLocalId';
import { useId } from '../../../useId';
import { DEFAULT_FILE_STATUS, fileUploaderClassnames } from '../../constants';
import { restrictFileMaxSize, restrictFileType } from '../../restrictions';
import { compareFile } from '../../utils/compareFile';
import { rulePipe } from '../../utils/rulePipe';
import { toArray } from '../../utils/toArray';
import { useDnD } from '../useDnD';
import { useUploadFiles } from '../useUploadFiles';
import { usePrepareAccept } from './hooks';
export const useFileUploader = ({ value, isDisabled, isMultiple, accept, restrictions = [], maxFileSize, maxFileCount = 1, // для случая, когда isMultiple=false
inputRef, notify, onChange, onUploadFile, onDeleteFile, name, isError, onView, generateDownloadLink, placeholder, helperText, label, className, getErrorMessage, }) => {
    const inputId = useId();
    const [deletingFileId, setDeletingFileId] = useState(null);
    const { generateId } = useGenerateLocalId();
    const acceptMeta = usePrepareAccept(accept);
    // Преобразуем value к массиву
    const files = useMemo(() => toArray(value), [value]);
    // true, если достигли лимита по кол-ву файлов при isMultiple=true
    const isLimitMaxFileCount = isMultiple ? files.length >= maxFileCount : false;
    const isVisibleAddFile = isMultiple ? !isLimitMaxFileCount : !files.length;
    const isVisibleFileList = Boolean(files.length);
    const restrict = (file) => {
        return rulePipe(restrictFileMaxSize(formatFileSizeToView, maxFileSize), restrictFileType(acceptMeta), ...restrictions)(file);
    };
    const declensionFile = declensionOfWords(['файл', 'файла', 'файлов']);
    const declensionAdded = declensionOfWords([
        'Добавлен',
        'Добавлено',
        'Добавлено',
    ]);
    const notifyWarning = (addedFilesLength, droppedFilesLength) => {
        const title = droppedFilesLength === 1
            ? 'Файл не загружен'
            : `${declensionAdded(addedFilesLength)} ${addedFilesLength} ${declensionFile(addedFilesLength)} из ${droppedFilesLength}`;
        notify?.warning(title, {
            content: 'Вы превысили максимально допустимое количество файлов для загрузки',
        });
    };
    const getInitialStatusUploadFile = (error) => {
        if (error) {
            return {
                ...DEFAULT_FILE_STATUS,
                isRestrictionError: Boolean(error),
                errorMsg: error.message,
            };
        }
        return DEFAULT_FILE_STATUS;
    };
    const { enqueueFiles, refetchFile, selfFiles, setErrorStatusFile } = useUploadFiles({
        files,
        isMultiple,
        onChange,
        onUploadFile,
        getErrorMessage,
    });
    const handleChange = (event) => {
        // Преобразуем к массиву и удаляем дубликаты
        const droppedFiles = Array.from(event.target.files || []).filter((droppedFile) => !files.find((file) => compareFile(droppedFile, file)));
        const addedFiles = droppedFiles
            .slice(0, maxFileCount - files.length)
            .reduce((acc, droppedFile) => {
            const error = restrict(droppedFile);
            return [
                // biome-ignore lint/performance/noAccumulatingSpread: <>
                ...acc,
                {
                    id: `${inputId}${generateId()}`,
                    name: droppedFile.name,
                    type: droppedFile.type,
                    size: droppedFile.size,
                    file: droppedFile,
                    status: getInitialStatusUploadFile(error),
                },
            ];
        }, []);
        if (isMultiple) {
            onChange([...files, ...addedFiles]);
        }
        else if (addedFiles.length) {
            onChange(addedFiles[0]);
        }
        if (addedFiles.length < droppedFiles.length) {
            notifyWarning(addedFiles.length, droppedFiles.length);
        }
        enqueueFiles(addedFiles);
    };
    const { isDragActive, ...dropZoneActions } = useDnD({
        isDisabled,
        onChange: (droppedFiles) => {
            handleChange({
                target: { files: droppedFiles },
            });
        },
    });
    const handleDelete = (fileId) => {
        const deleteFile = () => {
            if (isMultiple) {
                return onChange(files?.filter(({ id }) => id !== fileId));
            }
            return onChange(null);
        };
        if (!onDeleteFile) {
            return deleteFile();
        }
        setDeletingFileId(fileId);
        onDeleteFile(fileId)
            .then(deleteFile)
            .catch(() => notify.error('Ошибка удаления файла'))
            .finally(() => setDeletingFileId(null));
    };
    const getHandlerOnRetry = () => {
        if (onUploadFile) {
            return (fileId, file) => refetchFile({ id: fileId, file });
        }
        return undefined;
    };
    return {
        dropZoneProps: {
            isActive: !isDisabled && isDragActive,
            htmlFor: inputId,
            isDisabled,
            isError,
            className: fileUploaderClassnames.dropzone,
            ...dropZoneActions,
            isLimitMaxFileCount,
            maxFileCount: maxFileCount,
            isVisibleAddFile,
            isVisibleFileList,
            hintPlaceholderProps: {
                isMultiple,
                accept,
                maxFileCount: maxFileCount,
                maxFileSize,
                placeholder,
            },
            fileInputProps: {
                id: inputId,
                accept: accept.join(),
                ref: inputRef,
                value: '',
                onChange: handleChange,
                name,
                isMultiple,
                isDisabled,
            },
            uploadButtonProps: {
                isDisabled,
                htmlFor: inputId,
            },
            containerProps: {
                className,
                label,
                isError,
                helperText,
            },
        },
        fileListProps: {
            onDelete: handleDelete,
            onRetry: getHandlerOnRetry(),
            onView,
            generateDownloadLink,
            isMultiple,
            files,
            selfFiles,
            setErrorStatusFile,
            getErrorMessage,
            deletingFileId,
        },
    };
};
