import { jsxs, jsx } from 'react/jsx-runtime';
import React__default, { useMemo, useRef, useState, useCallback } from 'react';
import { fileUploadTheme } from './FileUpload.theme.js';
import { cn } from '../../../utils/cn.js';
import { CloudArrowUpIcon, DocumentIcon, XMarkIcon } from '@heroicons/react/24/outline';
import { Button } from '../../core/Button/Button.js';

/**
 * FileUpload component - Drag-and-drop file uploader with previews
 *
 * Features:
 * - Click to select files or drag-and-drop
 * - File type and size validation
 * - Image preview thumbnails
 * - Multiple file support
 * - Maximum file limits
 * - Accessible and keyboard-friendly
 * - Full theme customization
 *
 * @example
 * <FileUpload
 *   accept="image/*"
 *   maxSize={5000000}
 *   multiple
 *   onUpload={(files) => console.log(files)}
 * />
 */
const FileUpload = React__default.forwardRef(({ accept, multiple = false, maxSize, maxFiles, disabled = false, onUpload, onRemove, onError, showPreview = true, buttonText = "Choose files", helperText = "or drag and drop", description, className, theme: customTheme, ...props }, ref) => {
    // Merge custom theme with default theme
    const theme = useMemo(() => customTheme
        ? { ...fileUploadTheme, ...customTheme }
        : fileUploadTheme, [customTheme]);
    const inputRef = useRef(null);
    const [isDragActive, setIsDragActive] = useState(false);
    const [selectedFiles, setSelectedFiles] = useState([]);
    const [previewUrls, setPreviewUrls] = useState(new Map());
    // Convert accept prop to array
    const acceptTypes = Array.isArray(accept) ? accept : accept ? [accept] : [];
    const acceptString = acceptTypes.join(",");
    // Validate file type
    const isValidFileType = useCallback((file) => {
        if (acceptTypes.length === 0)
            return true;
        return acceptTypes.some((type) => {
            // Handle wildcards like "image/*"
            if (type.includes("/*")) {
                const [category] = type.split("/");
                return file.type.startsWith(`${category}/`);
            }
            // Handle file extensions like ".pdf"
            if (type.startsWith(".")) {
                return file.name.toLowerCase().endsWith(type.toLowerCase());
            }
            // Handle exact MIME types
            return file.type === type;
        });
    }, [acceptTypes]);
    // Validate file size
    const isValidFileSize = useCallback((file) => {
        if (!maxSize)
            return true;
        return file.size <= maxSize;
    }, [maxSize]);
    // Create preview URL for images
    const createPreview = useCallback((file) => {
        if (file.type.startsWith("image/")) {
            const url = URL.createObjectURL(file);
            setPreviewUrls((prev) => new Map(prev).set(file, url));
        }
    }, []);
    // Clean up preview URLs
    const revokePreview = useCallback((file) => {
        const url = previewUrls.get(file);
        if (url) {
            URL.revokeObjectURL(url);
            setPreviewUrls((prev) => {
                const newMap = new Map(prev);
                newMap.delete(file);
                return newMap;
            });
        }
    }, [previewUrls]);
    // Handle file selection/drop
    const handleFiles = useCallback((files) => {
        if (!files || files.length === 0)
            return;
        const fileArray = Array.from(files);
        const validFiles = [];
        // Validate each file
        for (const file of fileArray) {
            // Check file type
            if (!isValidFileType(file)) {
                const error = {
                    type: "file-invalid-type",
                    message: `File type not accepted: ${file.name}`,
                    file,
                };
                onError === null || onError === void 0 ? void 0 : onError(error);
                continue;
            }
            // Check file size
            if (!isValidFileSize(file)) {
                const maxSizeMB = maxSize ? (maxSize / 1000000).toFixed(2) : "0";
                const error = {
                    type: "file-too-large",
                    message: `File too large: ${file.name} (max ${maxSizeMB}MB)`,
                    file,
                };
                onError === null || onError === void 0 ? void 0 : onError(error);
                continue;
            }
            validFiles.push(file);
        }
        // Check max files limit
        if (maxFiles && selectedFiles.length + validFiles.length > maxFiles) {
            const error = {
                type: "too-many-files",
                message: `Maximum ${maxFiles} files allowed`,
            };
            onError === null || onError === void 0 ? void 0 : onError(error);
            return;
        }
        // Update selected files
        const newFiles = multiple
            ? [...selectedFiles, ...validFiles]
            : validFiles;
        setSelectedFiles(newFiles);
        // Create previews
        validFiles.forEach(createPreview);
        // Call onUpload callback
        onUpload === null || onUpload === void 0 ? void 0 : onUpload(validFiles);
    }, [
        isValidFileType,
        isValidFileSize,
        maxSize,
        maxFiles,
        selectedFiles,
        multiple,
        onUpload,
        onError,
        createPreview,
    ]);
    // Handle drag events
    const handleDragEnter = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        if (!disabled)
            setIsDragActive(true);
    }, [disabled]);
    const handleDragLeave = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        setIsDragActive(false);
    }, []);
    const handleDragOver = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
    }, []);
    const handleDrop = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        setIsDragActive(false);
        if (disabled)
            return;
        const files = e.dataTransfer.files;
        handleFiles(files);
    }, [disabled, handleFiles]);
    // Handle click to select files
    const handleClick = useCallback(() => {
        var _a;
        if (!disabled) {
            (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
        }
    }, [disabled]);
    // Handle input change
    const handleInputChange = useCallback((e) => {
        handleFiles(e.target.files);
        // Reset input value to allow selecting the same file again
        e.target.value = "";
    }, [handleFiles]);
    // Handle file removal
    const handleRemoveFile = useCallback((file) => {
        setSelectedFiles((prev) => prev.filter((f) => f !== file));
        revokePreview(file);
        onRemove === null || onRemove === void 0 ? void 0 : onRemove(file);
    }, [onRemove, revokePreview]);
    // Format file size
    const formatFileSize = (bytes) => {
        if (bytes === 0)
            return "0 Bytes";
        const k = 1024;
        const sizes = ["Bytes", "KB", "MB", "GB"];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
    };
    return (jsxs("div", { ref: ref, className: cn(theme.containerStyle, className), ...props, children: [jsx("input", { ref: inputRef, type: "file", accept: acceptString, multiple: multiple, onChange: handleInputChange, disabled: disabled, className: "sr-only", "aria-label": "File upload" }), jsx("div", { className: cn(theme.dropzoneStyle, isDragActive && theme.dropzoneActiveStyle, disabled && theme.dropzoneDisabledStyle), onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, onClick: handleClick, role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled, onKeyDown: (e) => {
                    if (e.key === "Enter" || e.key === " ") {
                        e.preventDefault();
                        handleClick();
                    }
                }, children: jsxs("div", { className: theme.contentStyle, children: [jsx("div", { className: "size-11 border border-border bg-white rounded-full flex items-center justify-center mb-4", children: jsx(CloudArrowUpIcon, { className: `size-6 text-[var(--color-text-secondary)]` }) }), jsxs("p", { className: "text-base font-medium text-[var(--color-text-primary)]", children: [jsx("span", { className: "text-[var(--color-primary)] font-semibold", children: buttonText }), " ", helperText] }), description && (jsx("p", { className: "text-sm text-[var(--color-text-muted)] mt-1", children: description }))] }) }), showPreview && selectedFiles.length > 0 && (jsx("div", { className: theme.previewContainerStyle, children: selectedFiles.map((file, index) => {
                    const previewUrl = previewUrls.get(file);
                    const isImage = file.type.startsWith("image/");
                    return (jsxs("div", { className: theme.previewItemStyle, children: [isImage && previewUrl ? (jsx("img", { src: previewUrl, alt: file.name, className: theme.previewImageStyle })) : (jsxs("div", { className: theme.previewFileStyle, children: [jsx(DocumentIcon, { className: "w-8 h-8 mx-auto mb-2 text-[var(--color-text-muted)]" }), jsx("span", { className: "text-sm", children: file.name }), jsx("span", { className: "text-sm text-[var(--color-text-placeholder)] mt-1", children: formatFileSize(file.size) })] })), jsx(Button, { variant: "filled", color: "danger", size: "sm", iconOnly: true, onClick: (e) => {
                                    e.stopPropagation();
                                    handleRemoveFile(file);
                                }, "aria-label": `Remove ${file.name}`, className: cn(theme.removeButtonStyle, "rounded-full !p-0 size-6"), children: jsx(XMarkIcon, { className: "size-4", strokeWidth: 2 }) })] }, `${file.name}-${index}`));
                }) }))] }));
});
FileUpload.displayName = "FileUpload";

export { FileUpload };
