'use strict';

var index = require('./index-pX0sBY0-.js');
var domUtils = require('./dom-utils-BdvOgC2b.js');
var tracking = require('./tracking-BPjaTlNR.js');

const fileUploadCss = () => `:host{font-family:var(--ifx-font-family, sans-serif)}.file-upload-wrapper{display:flex;flex-direction:column}.file-upload-wrapper.disabled{pointer-events:none}.file-upload-wrapper.disabled label,.file-upload-wrapper.disabled .file-upload-info{color:#575352}.file-upload-label{font-style:normal;font-weight:400;font-size:1rem;line-height:1.5rem;color:#1D1D1D}.file-upload-label .required{display:inline-block;margin-left:4px}.file-upload-label .required.error{color:#CD002F}.upload-dropzone{border:1px dashed #BFBBBB;padding:40px;text-align:center;cursor:pointer;color:#1D1D1D;transition:border-color 0.3s ease;border-radius:1px;background:#FFFFFF}.upload-dropzone ifx-icon{color:#0A8276}.upload-dropzone:hover{border:1px dashed #8D8786}.upload-dropzone.drag-over{border:1px solid #0A8276;background:#F7F7F7}.upload-dropzone.drag-over.error{border:1px solid #0A8276}.upload-dropzone.error{border-color:#CD002F}.disabled .upload-dropzone{pointer-events:none;border-color:#BFBBBB;color:#BFBBBB}.disabled .upload-dropzone ifx-icon{color:#BFBBBB}.upload-dropzone input{display:none}.file-upload-info{font-style:normal;font-weight:400;font-size:0.75rem;line-height:1rem;letter-spacing:0em;color:#1D1D1D;margin:0;margin-top:4px}.file-upload-status{font-style:normal;font-weight:400;font-size:0.75rem;line-height:1rem;letter-spacing:0.2px;margin-top:8px}.file-upload-status.file-upload-status__error{display:flex;align-items:center;color:#CD002F}.file-upload-status.file-upload-status__error ifx-icon{position:relative;top:-1px;color:#CD002F;margin-right:8px}.upload-heading{font:600 1.125rem/1.625rem "Source Sans 3";margin-bottom:8px}.file-list-wrapper{margin-top:16px}.file-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:16px}.file-item{border:1px solid #BFBBBB;padding-top:12px;padding-right:16px;padding-bottom:8px;padding-left:16px;display:flex;gap:8px}.file-info{width:100%;min-width:0}.file-top-row{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:-8px}.file-top-row ifx-icon{flex-shrink:0}.file-name-wrapper{display:flex;min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;font-weight:400;font-size:0.875rem}.file-name-base{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:1;min-width:0}.file-name-ext{flex-shrink:0;margin-left:0}.file-middle-row{display:flex;align-items:center;font-size:0.75rem;line-height:1rem;flex-wrap:wrap;color:#575352}.file-middle-row .file-status{margin-left:12px}.file-middle-row .file-status ifx-icon{margin-right:8px}.file-progress-row{margin-top:4px}.file-progress-row ifx-progress-bar{width:100%}.upload-success{border-color:#0A8276}.upload-success .file-status ifx-icon{position:relative;top:1px;color:#4CA460}.upload-failed{border-color:#CD002F}.upload-failed .file-status{color:#CD002F;margin-left:0}`;

const FileUpload = class {
    constructor(hostRef) {
        index.registerInstance(this, hostRef);
        this.ifxFileUploadAdd = index.createEvent(this, "ifxFileUploadAdd", 7);
        this.ifxFileUploadRemove = index.createEvent(this, "ifxFileUploadRemove", 7);
        this.ifxFileUploadChange = index.createEvent(this, "ifxFileUploadChange", 7);
        this.ifxFileUploadError = index.createEvent(this, "ifxFileUploadError", 7);
        this.ifxFileUploadInvalid = index.createEvent(this, "ifxFileUploadInvalid", 7);
        this.ifxFileUploadStart = index.createEvent(this, "ifxFileUploadStart", 7);
        this.ifxFileUploadComplete = index.createEvent(this, "ifxFileUploadComplete", 7);
        this.ifxFileUploadAllComplete = index.createEvent(this, "ifxFileUploadAllComplete", 7);
        this.ifxFileUploadAbort = index.createEvent(this, "ifxFileUploadAbort", 7);
        this.ifxFileUploadDrop = index.createEvent(this, "ifxFileUploadDrop", 7);
        this.ifxFileUploadClick = index.createEvent(this, "ifxFileUploadClick", 7);
        this.ifxFileUploadMaxFilesExceeded = index.createEvent(this, "ifxFileUploadMaxFilesExceeded", 7);
        this.ifxFileUploadValidation = index.createEvent(this, "ifxFileUploadValidation", 7);
        this.ifxFileUploadRetry = index.createEvent(this, "ifxFileUploadRetry", 7);
    }
    get hostElement() { return index.getElement(this); }
    /** Enables drag-and-drop for file uploads. */
    dragAndDrop = false;
    /** Wether at least one file is rewuired. */
    required = false;
    /** If true, the upload is disabled and not interactive. */
    disabled = false;
    /** Maximum file size allowed in megabytes. */
    maxFileSizeMB = 7;
    /** Default set of allowed file extensions (used internally). Can be extended using `additionalAllowedFileTypes`. */
    allowedFileTypes = undefined;
    /** Extra file types to allow in addition to the  default ones. */
    additionalAllowedFileTypes = [];
    /** When set to true, allows any file type to be uploaded (no file type restrictions). */
    allowAnyFileType = false;
    /** Custom file extensions to allow (e.g., 'xml', 'asc', 'cfg'). Recommended format: without dots. Also accepts format with dots like '.xml'. Do not use wildcards like '*.xml'. */
    allowedFileExtensions = [];
    /** Custom function that handels file upload and progress reporting. */
    uploadHandler;
    _maxFiles;
    get maxFiles() {
        return this._maxFiles;
    }
    set maxFiles(value) {
        if (typeof value === "number" && value < 1) {
            console.warn("Invalid `maxFiles` value. Must be >= 1. Value ignored.");
            this._maxFiles = undefined;
        }
        else {
            this._maxFiles = value;
        }
    }
    /** Main label for the upload component. */
    label = "Label";
    /** Error message shown when no file is uploaded but at least one is required. */
    labelRequiredError = "At least one file must be uploaded";
    /** Text for the “browse files” button or link. */
    labelBrowseFiles = "Browse files";
    /** Text shown in the drop zone area. */
    labelDragAndDrop = "Drag & Drop or browse files to upload";
    /** Heading label above the list of uploaded files. */
    labelUploadedFilesHeading = "Uploaded files";
    /** Message shown when a file is too large ({{size}} is replaced with max size). */
    labelFileTooLarge = "Upload failed. Max file size: {{size}}MB.";
    /** Message shown when the file type is not allowed. */
    labelUnsupportedFileType = "Unsupported file type.";
    /** Status text for a successfully uploaded file. */
    labelUploaded = "Successfully uploaded";
    /** Generic error message shown when upload fails. */
    labelUploadFailed = "Upload failed. Please try again.";
    /** Template text for listing supported formats and size ({{types}} and {{size}} are replaced). */
    labelSupportedFormatsTemplate = "Supported file formats: {{types}}. Max file size: {{size}}MB.";
    /** Singular word used for “file” in messages. */
    labelFileSingular = "file";
    /** Plural word used for “files” in messages. */
    labelFilePlural = "files";
    /** Info text about the maximum number of files allowed ({{count}} and {{files}} are replaced). */
    labelMaxFilesInfo = "Up to {{count}} {{files}}.";
    /** Error message shown when the max number of files is exceeded. */
    labelMaxFilesExceeded = "Upload limit exceeded. Only {{count}} {{files}} allowed.";
    /** ARIA label for the “browse files” control. */
    ariaLabelBrowseFiles = "Browse files";
    /** ARIA label for the drop zone area. */
    ariaLabelDropzone = "Upload area. Click to browse or drag and drop files.";
    /** ARIA label for the hidden/native file input element. */
    ariaLabelFileInput = "Upload file";
    /** ARIA label for the “remove file” action. */
    ariaLabelRemoveFile = "Remove file";
    /** ARIA label for the “cancel upload” action. */
    ariaLabelCancelUpload = "Cancel upload";
    /** ARIA label for the “retry upload” action. */
    ariaLabelRetryUpload = "Retry upload";
    /** ARIA label describing that an upload is currently in progress. */
    ariaLabelUploadingStatus = "Upload in progress";
    /** ARIA label describing that upload has completed successfully. */
    ariaLabelUploadedStatus = "Upload completed";
    /** ARIA label describing that the upload has failed. */
    ariaLabelUploadFailedStatus = "Upload failed";
    showDemoStates;
    internalId = `ifx-file-upload-${Math.random().toString(36).substr(2, 9)}`;
    isDragOver = false;
    files = [];
    uploadTasks = [];
    rejectedSizeFiles = [];
    rejectedTypeFiles = [];
    requiredError = false;
    statusMessage = null;
    /** Fired when files are added (e.g. via browse or drop). */
    ifxFileUploadAdd;
    /** Fired when a file is removed from the list. */
    ifxFileUploadRemove;
    /** Fired whenever the list of selected files changes. */
    ifxFileUploadChange;
    /** Fired when an upload-related error occurs. */
    ifxFileUploadError;
    /** Fired when a file fails validation before upload. */
    ifxFileUploadInvalid;
    /** Fired when upload starts for a file. */
    ifxFileUploadStart;
    /** Fired when a single file upload finishes successfully. */
    ifxFileUploadComplete;
    /** Fired when all file uploads have finished successfully. */
    ifxFileUploadAllComplete;
    /** Fired when an ongoing upload is aborted/cancelled. */
    ifxFileUploadAbort;
    /** Fired when files are dropped onto the drop zone. */
    ifxFileUploadDrop;
    /** Fired when the upload area is clicked (typically to open file dialog). */
    ifxFileUploadClick;
    /** Fired when the user tries to add more than the allowed number of files. */
    ifxFileUploadMaxFilesExceeded;
    /** Fired after validating the current files (valid or invalid). */
    ifxFileUploadValidation;
    /** Fired when the user retries uploading a file after a failure. */
    ifxFileUploadRetry;
    fileInputEl = null;
    extensionToMimeMap = {
        /**
         * Maps file extensions to MIME types.
         * This is only used for translating `allowedFileTypes` (extensions) into MIME types,
         * and for labeling in the UI. It does NOT define which files are globally allowed.
         */
        // Images
        jpg: "image/jpeg",
        jpeg: "image/jpeg",
        png: "image/png",
        gif: "image/gif",
        svg: "image/svg+xml",
        webp: "image/webp",
        // Documents
        pdf: "application/pdf",
        doc: "application/msword",
        docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        xls: "application/vnd.ms-excel",
        xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        ppt: "application/vnd.ms-powerpoint",
        pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
        txt: "text/plain",
        csv: "text/csv",
        json: "application/json",
        // Audio/Video
        mp3: "audio/mpeg",
        wav: "audio/wav",
        mp4: "video/mp4",
        mov: "video/quicktime",
        webm: "video/webm",
        // Archive / Code
        zip: "application/zip",
        rar: "application/vnd.rar",
        tar: "application/x-tar",
        gz: "application/gzip",
        // Sonstiges
        xml: "application/xml",
        html: "text/html",
        css: "text/css",
        js: "application/javascript",
    };
    validateRequired() {
        if (this.required && this.files.length === 0) {
            this.requiredError = true;
            if (this.statusMessage?.text !== this.labelRequiredError) {
                this.statusMessage = {
                    type: "error",
                    text: this.labelRequiredError,
                };
            }
            this.ifxFileUploadValidation.emit({ valid: false });
        }
        else {
            this.requiredError = false;
            if (this.statusMessage?.text === this.labelRequiredError) {
                this.statusMessage = null;
            }
            this.ifxFileUploadValidation.emit({ valid: true });
        }
    }
    pluralize(count) {
        // If allowAnyFileType is true, use generic terms
        if (this.allowAnyFileType) {
            return count === 1 ? "file" : "files";
        }
        return count === 1 ? this.labelFileSingular : this.labelFilePlural;
    }
    getNormalizedFileTypes() {
        // If allowedFileTypes is not set and allowedFileExtensions is set, return empty array
        if (!this.allowedFileTypes &&
            this.allowedFileExtensions &&
            this.getNormalizedFileExtensions().length > 0) {
            return [];
        }
        // If allowedFileTypes is not set and no allowedFileExtensions, use defaults
        if (!this.allowedFileTypes) {
            return ["jpg", "jpeg", "png", "pdf", "mov", "mp3", "mp4"];
        }
        if (Array.isArray(this.allowedFileTypes)) {
            return this.allowedFileTypes;
        }
        try {
            return JSON.parse(this.allowedFileTypes);
        }
        catch {
            return this.allowedFileTypes.split(",").map((t) => t.trim());
        }
    }
    getNormalizedFileExtensions() {
        if (!this.allowedFileExtensions)
            return [];
        if (Array.isArray(this.allowedFileExtensions)) {
            return this.allowedFileExtensions;
        }
        try {
            return JSON.parse(this.allowedFileExtensions);
        }
        catch {
            return this.allowedFileExtensions.split(",").map((t) => t.trim());
        }
    }
    /**
     * Enhanced file type validation that supports:
     * - allowAnyFileType flag for unrestricted uploads (overrides all restrictions)
     * - allowedFileTypes (predefined extensions mapped to MIME types) - if not set and allowedFileExtensions is set, ignored
     * - additionalAllowedFileTypes (MIME types)
     * - allowedFileExtensions (custom extensions) - if only this is set, only these extensions are allowed
     */
    isFileTypeAllowed(file) {
        // If allowAnyFileType is true, accept all files (overrides all restrictions)
        if (this.allowAnyFileType) {
            return true;
        }
        const fileExtension = this.getFileExtension(file.name);
        // Check against allowedFileTypes (predefined extensions)
        const normalizedTypes = this.getNormalizedFileTypes();
        const allowedMimes = normalizedTypes
            .map((ext) => this.extensionToMimeMap[ext.toLowerCase()])
            .filter(Boolean);
        if (allowedMimes.includes(file.type)) {
            return true;
        }
        // Check against additionalAllowedFileTypes (MIME types)
        const additionalMimeTypes = this.getAdditionalMimeTypes();
        if (additionalMimeTypes.includes(file.type)) {
            return true;
        }
        // Check against allowedFileExtensions (custom extensions)
        const customExtensions = this.getNormalizedFileExtensions();
        if (customExtensions.length > 0 && fileExtension) {
            for (const ext of customExtensions) {
                const normalizedExt = ext.startsWith(".")
                    ? ext.substring(1).toLowerCase()
                    : ext.toLowerCase();
                if (fileExtension === normalizedExt) {
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * Extracts file extension from filename (without dot)
     */
    getFileExtension(filename) {
        const lastDotIndex = filename.lastIndexOf(".");
        if (lastDotIndex === -1 || lastDotIndex === filename.length - 1) {
            return null;
        }
        return filename.substring(lastDotIndex + 1).toLowerCase();
    }
    getLabelFromMimeType(mime) {
        for (const [ext, knownMime] of Object.entries(this.extensionToMimeMap)) {
            if (knownMime === mime) {
                return ext.toUpperCase();
            }
        }
        return mime; // fallback: show raw MIME
    }
    handleFileChange(event) {
        const input = event.target;
        if (!input.files)
            return;
        this.processFiles(input.files);
    }
    handleDrop(event) {
        if (this.disabled)
            return;
        event.preventDefault();
        event.stopPropagation();
        this.isDragOver = false;
        if (event.dataTransfer?.files) {
            const droppedFiles = Array.from(event.dataTransfer.files);
            const acceptedFiles = [];
            const rejectedFiles = [];
            droppedFiles.forEach((file) => {
                const isValidType = this.isFileTypeAllowed(file);
                const isValidSize = file.size <= this.maxFileSizeMB * 1024 * 1024;
                if (isValidType && isValidSize)
                    acceptedFiles.push(file);
                else
                    rejectedFiles.push(file);
            });
            this.ifxFileUploadDrop.emit({
                droppedFiles,
                acceptedFiles,
                rejectedFiles,
            });
            this.processFiles(event.dataTransfer.files);
        }
    }
    handleDragOver(event) {
        if (this.disabled)
            return;
        event.preventDefault();
        this.isDragOver = true;
    }
    handleDragLeave(event) {
        if (this.disabled)
            return;
        event.preventDefault();
        this.isDragOver = false;
    }
    processFiles(fileList) {
        const selectedFiles = Array.from(fileList);
        const validFiles = [];
        const rejectedSize = [];
        const rejectedType = [];
        selectedFiles.forEach((file) => {
            const isValidType = this.isFileTypeAllowed(file);
            const isValidSize = file.size <= this.maxFileSizeMB * 1024 * 1024;
            const isDuplicate = this.files.some((existing) => existing.name === file.name && existing.size === file.size);
            if (isDuplicate) {
                this.ifxFileUploadInvalid.emit({ file, reason: "duplicate" });
                this.ifxFileUploadError.emit({
                    file,
                    errorType: "duplicate",
                    message: `File "${file.name}" is already added`,
                    reason: "duplicate",
                });
                return;
            }
            if (isValidType && isValidSize) {
                validFiles.push(file);
            }
            else {
                if (!isValidType) {
                    rejectedType.push(file.name);
                    this.ifxFileUploadInvalid.emit({ file, reason: "invalid-type" });
                }
                if (!isValidSize) {
                    rejectedSize.push(file.name);
                    this.ifxFileUploadInvalid.emit({ file, reason: "invalid-size" });
                }
                this.ifxFileUploadError.emit({
                    file,
                    errorType: !isValidType ? "invalid-type" : "file-too-large",
                    message: "Invalid file rejected",
                    reason: !isValidType ? "unsupported-type" : "file-too-large",
                });
            }
        });
        this.rejectedSizeFiles = [...this.rejectedSizeFiles, ...rejectedSize];
        this.rejectedTypeFiles = [...this.rejectedTypeFiles, ...rejectedType];
        if (this.maxFiles &&
            this.files.length + validFiles.length > this.maxFiles) {
            const availableSlots = this.maxFiles - this.files.length;
            const limitedFiles = validFiles.slice(0, Math.max(availableSlots, 0));
            const overflowFiles = validFiles.slice(availableSlots);
            this.files = [...this.files, ...limitedFiles];
            limitedFiles.forEach((file) => this.startUpload(file));
            if (limitedFiles.length > 0) {
                this.ifxFileUploadAdd.emit({
                    addedFiles: limitedFiles,
                    files: this.files,
                });
                this.ifxFileUploadChange.emit({ files: this.files });
            }
            overflowFiles.forEach((file) => {
                this.ifxFileUploadInvalid.emit({ file, reason: "too-many-files" });
                this.ifxFileUploadError.emit({
                    file,
                    errorType: "too-many-files",
                    message: `Upload limit exceeded. Max ${this.maxFiles} files allowed.`,
                    reason: "too-many-files",
                });
            });
            if (overflowFiles.length > 0) {
                this.statusMessage = {
                    type: "error",
                    text: this.labelMaxFilesExceeded
                        .replace("{{count}}", this.maxFiles.toString())
                        .replace("{{files}}", this.pluralize(this.maxFiles)),
                };
                this.ifxFileUploadMaxFilesExceeded.emit({
                    maxFiles: this.maxFiles,
                    attempted: this.files.length + validFiles.length,
                });
            }
            return;
        }
        validFiles.forEach((file) => this.startUpload(file));
        this.files = [...this.files, ...validFiles];
        if (validFiles.length > 0) {
            this.ifxFileUploadAdd.emit({ addedFiles: validFiles, files: this.files });
            this.ifxFileUploadChange.emit({ files: this.files });
        }
        this.validateRequired();
    }
    retryUpload(file) {
        const taskIndex = this.uploadTasks.findIndex((t) => t.file.name === file.name);
        if (taskIndex !== -1) {
            this.uploadTasks.splice(taskIndex, 1);
            this.uploadTasks = [...this.uploadTasks];
        }
        this.ifxFileUploadRetry.emit({ file });
        this.startUpload(file);
    }
    checkAndEmitAllComplete() {
        if (this.uploadTasks.every((t) => t.completed || t.error)) {
            const hasSuccessfulUploads = this.uploadTasks.some((t) => t.completed && !t.error);
            if (hasSuccessfulUploads) {
                const successfulFiles = this.uploadTasks
                    .filter((t) => t.completed && !t.error)
                    .map((t) => t.file);
                this.ifxFileUploadAllComplete.emit({ files: successfulFiles });
            }
        }
    }
    resetFileInput() {
        if (this.fileInputEl) {
            this.fileInputEl.value = "";
        }
    }
    updateTaskProgress(file, progress) {
        const task = this.uploadTasks.find((t) => t.file === file);
        if (task) {
            const newProgress = Math.max(task.progress, Math.min(100, progress));
            if (newProgress !== task.progress) {
                task.progress = newProgress;
                this.uploadTasks = [...this.uploadTasks];
            }
        }
    }
    startUpload(file) {
        this.ifxFileUploadStart.emit({ file });
        const task = {
            file,
            progress: 3, // Start with initial progress for better UX
            intervalId: null,
            completed: false,
        };
        this.uploadTasks = [...this.uploadTasks, task];
        if (this.uploadHandler) {
            this.uploadHandler(file, (percent) => {
                this.updateTaskProgress(file, percent);
            })
                .then(() => {
                task.progress = 100;
                task.completed = true;
                this.uploadTasks = [...this.uploadTasks];
                this.ifxFileUploadComplete.emit({ file });
                this.ifxFileUploadChange.emit({ files: this.files });
                this.checkAndEmitAllComplete();
            })
                .catch(() => {
                task.error = true;
                this.uploadTasks = [...this.uploadTasks];
                this.ifxFileUploadError.emit({
                    file,
                    errorType: "upload-failed",
                    message: "Upload handler rejected file",
                    reason: "custom",
                });
                this.checkAndEmitAllComplete();
            });
        }
        else {
            const totalSize = file.size;
            const fakeUploadSpeed = 100000;
            let uploaded = 0;
            task.intervalId = window.setInterval(() => {
                uploaded += fakeUploadSpeed / 5;
                const progress = Math.min(100, Math.round((uploaded / totalSize) * 100));
                this.updateTaskProgress(file, progress);
                if (progress >= 100) {
                    clearInterval(task.intervalId);
                    task.completed = true;
                    task.intervalId = null;
                    this.uploadTasks = [...this.uploadTasks];
                    this.ifxFileUploadComplete.emit({ file });
                    this.ifxFileUploadChange.emit({ files: this.files });
                    this.checkAndEmitAllComplete();
                }
            }, 200);
        }
    }
    cancelUpload(file) {
        const taskIndex = this.uploadTasks.findIndex((t) => t.file.name === file.name);
        if (taskIndex !== -1) {
            const task = this.uploadTasks[taskIndex];
            if (task?.intervalId !== null) {
                clearInterval(task.intervalId);
            }
            this.uploadTasks = this.uploadTasks.filter((_, i) => i !== taskIndex);
        }
        this.files = this.files.filter((f) => f.name !== file.name);
        this.ifxFileUploadAbort.emit({ file });
        this.ifxFileUploadChange.emit({ files: this.files });
        this.resetFileInput();
        this.validateRequired();
    }
    removeFile(file) {
        this.uploadTasks = this.uploadTasks.filter((task) => task.file.name !== file.name);
        this.files = this.files.filter((f) => f.name !== file.name);
        this.ifxFileUploadRemove.emit({ removedFile: file, files: this.files });
        this.ifxFileUploadChange.emit({ files: this.files });
        this.validateRequired();
        this.resetFileInput();
        if (this.maxFiles &&
            this.files.length < this.maxFiles &&
            this.statusMessage?.text !== this.labelRequiredError) {
            this.statusMessage = null;
        }
    }
    clearRejectedFile(fileName, type) {
        if (type === "size") {
            this.rejectedSizeFiles = this.rejectedSizeFiles.filter((f) => f !== fileName);
        }
        else {
            this.rejectedTypeFiles = this.rejectedTypeFiles.filter((f) => f !== fileName);
        }
        this.resetFileInput();
        if (this.maxFiles && this.files.length < this.maxFiles) {
            this.statusMessage = null;
        }
        this.validateRequired();
    }
    splitFileNameParts(file) {
        const name = file.name;
        const dotIndex = name.lastIndexOf(".");
        if (dotIndex === -1)
            return { base: name, ext: "" };
        return {
            base: name.substring(0, dotIndex),
            ext: name.substring(dotIndex),
        };
    }
    getFileIcon(file) {
        const extension = file.name.split(".").pop()?.toLowerCase();
        switch (extension) {
            case "pdf":
                return "file-pdf-16";
            case "jpg":
            case "jpeg":
                return "file-jpg-16";
            case "png":
                return "file-png-16";
            case "mov":
                return "file-mov-16";
            case "mp3":
                return "file-mp3-16";
            case "mp4":
                return "file-mp4-16";
            default:
                return "file-16";
        }
    }
    formatSize(bytes) {
        if (bytes >= 1024 * 1024 * 1024) {
            return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
        }
        else if (bytes >= 1024 * 1024) {
            return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
        }
        else if (bytes >= 1024) {
            return `${(bytes / 1024).toFixed(0)} KB`;
        }
        else {
            return `${bytes} B`;
        }
    }
    getAcceptAttribute() {
        // If allowAnyFileType is true, don't restrict the input
        if (this.allowAnyFileType) {
            return "";
        }
        const acceptValues = [];
        // Add extensions from allowedFileTypes
        const extensionTypes = this.getNormalizedFileTypes().map((ext) => "." + ext.toLowerCase());
        acceptValues.push(...extensionTypes);
        // Add MIME types from additionalAllowedFileTypes
        const mimeTypes = this.getAdditionalMimeTypes();
        acceptValues.push(...mimeTypes);
        // Add custom file extensions
        const customExtensions = this.getNormalizedFileExtensions();
        customExtensions.forEach((ext) => {
            const normalizedExt = ext.startsWith(".") ? ext : "." + ext;
            acceptValues.push(normalizedExt);
        });
        return acceptValues.join(",");
    }
    getFormattedProgressText(task) {
        const uploadedSize = Math.round((task.progress / 100) * task.file.size);
        const uploadedText = this.formatSize(uploadedSize);
        const totalText = this.formatSize(task.file.size);
        return `${uploadedText} / ${totalText} uploaded`;
    }
    getAdditionalMimeTypes() {
        if (!this.additionalAllowedFileTypes)
            return [];
        if (Array.isArray(this.additionalAllowedFileTypes))
            return this.additionalAllowedFileTypes;
        return this.additionalAllowedFileTypes.split(",").map((t) => t.trim());
    }
    getSupportedFileText() {
        if (this.allowAnyFileType) {
            let text = `All file types allowed. Max file size: ${this.maxFileSizeMB}MB.`;
            if (this.labelMaxFilesInfo && this.maxFiles) {
                const fileWord = this.pluralize(this.maxFiles);
                const maxFilesText = this.labelMaxFilesInfo
                    .replace("{{count}}", this.maxFiles.toString())
                    .replace("{{files}}", fileWord);
                text += ` ${maxFilesText}`;
            }
            return text;
        }
        const allTypes = [];
        // Add extensions from allowedFileTypes
        const extensions = this.getNormalizedFileTypes().map((ext) => ext.toUpperCase());
        allTypes.push(...extensions);
        // Add MIME types from additionalAllowedFileTypes
        const mimeTypes = this.getAdditionalMimeTypes().map((mime) => this.getLabelFromMimeType(mime));
        allTypes.push(...mimeTypes);
        // Add custom file extensions
        const customExtensions = this.getNormalizedFileExtensions().map((ext) => {
            const cleanExt = ext.startsWith(".") ? ext.substring(1) : ext;
            return cleanExt.toUpperCase();
        });
        allTypes.push(...customExtensions);
        const typesLabel = allTypes.join(", ");
        let text = this.labelSupportedFormatsTemplate
            .replace("{{types}}", typesLabel)
            .replace("{{size}}", this.maxFileSizeMB.toString());
        if (this.labelMaxFilesInfo && this.maxFiles) {
            const fileWord = this.pluralize(this.maxFiles);
            const maxFilesText = this.labelMaxFilesInfo
                .replace("{{count}}", this.maxFiles.toString())
                .replace("{{files}}", fileWord);
            text += ` ${maxFilesText}`;
        }
        return text;
    }
    getFormattedFileTooLargeText() {
        return this.labelFileTooLarge.replace("{{size}}", this.maxFileSizeMB.toString());
    }
    renderStatusMessage() {
        if (!this.statusMessage)
            return null;
        return (index.h("div", { class: `file-upload-status file-upload-status__${this.statusMessage.type}` }, this.statusMessage.type === "error" && (index.h("ifx-icon", { icon: "c-warning-16" })), this.statusMessage.text));
    }
    isInputDisabled() {
        return (this.disabled ||
            (this.maxFiles !== undefined && this.files.length >= this.maxFiles));
    }
    async componentDidLoad() {
        if (!domUtils.isNestedInIfxComponent(this.hostElement)) {
            const framework = tracking.detectFramework();
            tracking.trackComponent("ifx-file-upload", await framework);
        }
        if (this.hostElement.hasAttribute("show-demo-states")) {
            this.showDemoStates = true;
        }
        if (this.showDemoStates) {
            this.injectDemoState();
        }
    }
    /** Storybook Demo */
    async injectDemoState() {
        const dummyContent = new Array(50000).fill("a").join(""); // ~50 KB
        const bigContent = dummyContent + dummyContent; // ~100 KB
        const uploading = new File([dummyContent], "Image.jpg", {
            type: "image/jpeg",
        }); // ~50 KB
        const uploaded = new File([bigContent], "File.pdf", {
            type: "application/pdf",
        }); // ~100 KB
        const failed = new File([dummyContent], "Text.docx", {
            type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        }); // ~50 KB
        const tooLarge = new File([bigContent + bigContent + bigContent], "Video.mp4", { type: "video/mp4" }); // ~300 KB
        const unsupported = new File(["demo"], "Script.exe", {
            type: "application/x-msdownload",
        }); // very small
        this.files = [uploaded, uploading, failed];
        this.uploadTasks = [
            { file: uploaded, progress: 100, intervalId: null, completed: true },
            { file: uploading, progress: 40, intervalId: null, completed: false },
            {
                file: failed,
                progress: 80,
                intervalId: null,
                completed: false,
                error: true,
            },
        ];
        this.rejectedSizeFiles = [tooLarge.name];
        this.rejectedTypeFiles = [unsupported.name];
    }
    /** Storybook Demo */
    async triggerDemoValidation() {
        this.validateRequired();
    }
    render() {
        return (index.h("div", { key: '83718c2c909280b043023f57f1070810a65a7fa7', class: {
                "file-upload-wrapper": true,
                disabled: this.disabled,
            } }, this.label && (index.h("label", { key: 'e9a12258a1414e66241a529a07992cb46cef174f', class: "file-upload-label", htmlFor: this.internalId }, this.label, this.required && (index.h("span", { key: '32e0c0595ad82eddd57178dd8d088ae076dc69b1', class: `required ${this.requiredError ? "error" : ""}` }, "*")))), this.dragAndDrop
            ? this.renderDragAndDropArea()
            : this.renderUploadArea(), (this.files.length > 0 ||
            this.rejectedSizeFiles.length > 0 ||
            this.rejectedTypeFiles.length > 0) && (index.h("div", { key: '0e9be92dcbd5ca5b22d8152ec0af36061bdee26c', class: "file-list-wrapper" }, index.h("div", { key: '1a76ee7ce5dbfe4740f454b544a17e3c3f31df46', class: "upload-heading" }, this.labelUploadedFilesHeading), index.h("ul", { key: 'c0e2076bf2f6ecef09a56a669fd42fef0b99fa98', class: "file-list" }, this.rejectedSizeFiles.map((fileName) => (index.h("li", { class: "file-item upload-failed", key: `rejected-size-${fileName}` }, index.h("div", { class: "file-icon" }, index.h("ifx-icon", { icon: "file-16" })), index.h("div", { class: "file-info" }, index.h("div", { class: "file-top-row" }, index.h("span", { class: "file-name-wrapper" }, index.h("span", { class: "file-name-base" }, this.splitFileNameParts({ name: fileName })
            .base), index.h("span", { class: "file-name-ext" }, this.splitFileNameParts({ name: fileName })
            .ext)), index.h("div", { class: "file-actions" }, index.h("ifx-icon-button", { shape: "square", variant: "tertiary", icon: "delete-forever-16", size: "s", "aria-label": this.ariaLabelRemoveFile, onClick: () => this.clearRejectedFile(fileName, "size") }))), index.h("div", { class: "file-middle-row" }, index.h("span", { class: "file-status", "aria-label": this.labelFileTooLarge.replace("{{size}}", this.maxFileSizeMB.toString()) }, this.getFormattedFileTooLargeText())))))), this.rejectedTypeFiles.map((fileName) => (index.h("li", { class: "file-item upload-failed", key: `rejected-type-${fileName}` }, index.h("div", { class: "file-icon" }, index.h("ifx-icon", { icon: "file-16" })), index.h("div", { class: "file-info" }, index.h("div", { class: "file-top-row" }, index.h("span", { class: "file-name-wrapper" }, index.h("span", { class: "file-name-base" }, this.splitFileNameParts({ name: fileName })
            .base), index.h("span", { class: "file-name-ext" }, this.splitFileNameParts({ name: fileName })
            .ext)), index.h("div", { class: "file-actions" }, index.h("ifx-icon-button", { shape: "square", variant: "tertiary", icon: "delete-forever-16", size: "s", "aria-label": this.ariaLabelRemoveFile, onClick: () => this.clearRejectedFile(fileName, "type") }))), index.h("div", { class: "file-middle-row" }, index.h("span", { class: "file-status", "aria-label": this.labelUnsupportedFileType }, this.labelUnsupportedFileType)))))), this.files.map((file) => {
            const task = this.uploadTasks.find((t) => t.file.name === file.name);
            const progress = task?.progress ?? 100;
            const isUploading = task && !task.completed;
            const isError = task?.error === true;
            const itemClass = isError
                ? "file-item upload-failed"
                : isUploading
                    ? "file-item uploading"
                    : "file-item upload-success";
            const uniqueKey = `${file.name}-${file.size}`;
            const { base, ext } = this.splitFileNameParts(file);
            return (index.h("li", { class: itemClass, key: uniqueKey }, index.h("div", { class: "file-icon" }, index.h("ifx-icon", { icon: this.getFileIcon(file) })), index.h("div", { class: "file-info" }, index.h("div", { class: "file-top-row" }, index.h("span", { class: "file-name-wrapper" }, index.h("span", { class: "file-name-base" }, base), index.h("span", { class: "file-name-ext" }, ext)), index.h("div", { class: "file-actions" }, index.h("ifx-icon-button", { shape: "square", variant: "tertiary", icon: "refresh-16", size: "s", "aria-label": this.ariaLabelRetryUpload, onClick: () => this.retryUpload(file), style: {
                    display: isError ? "inline-flex" : "none",
                } }), index.h("ifx-icon-button", { shape: "square", variant: "tertiary", icon: "cross-16", size: "s", "aria-label": this.ariaLabelCancelUpload, onClick: () => this.cancelUpload(file), style: {
                    display: isUploading ? "inline-flex" : "none",
                } }), index.h("ifx-icon-button", { shape: "square", variant: "tertiary", icon: "delete-forever-16", size: "s", "aria-label": this.ariaLabelRemoveFile, onClick: () => this.removeFile(file), style: {
                    display: !isUploading ? "inline-flex" : "none",
                } }))), index.h("div", { class: "file-middle-row" }, isUploading && task && !task.error && (index.h("span", { class: "file-uploading", "aria-label": this.ariaLabelUploadingStatus }, this.getFormattedProgressText(task))), !isUploading && !isError && (index.h("span", null, index.h("span", { class: "file-size" }, this.formatSize(file.size)), index.h("span", { class: "file-status", "aria-label": this.ariaLabelUploadedStatus }, index.h("ifx-icon", { icon: "check-16" }), this.labelUploaded))), isError && (index.h("span", { class: "file-status", "aria-label": this.ariaLabelUploadFailedStatus }, this.labelUploadFailed))), isUploading && task && !task.error && (index.h("div", { class: "file-progress-row" }, index.h("ifx-progress-bar", { size: "s", value: progress, "show-label": "true" }))))));
        }))))));
    }
    renderUploadArea() {
        const handleInputRef = (el) => {
            this.fileInputEl = el;
        };
        return (index.h("div", { class: { "upload-button": true } }, index.h("ifx-button", { variant: "secondary", onClick: () => this.fileInputEl?.click(), disabled: this.isInputDisabled(), "aria-label": this.ariaLabelBrowseFiles }, index.h("ifx-icon", { icon: "upload-16" }), this.labelBrowseFiles), index.h("input", { id: this.internalId, ref: handleInputRef, type: "file", accept: this.getAcceptAttribute(), multiple: true, onChange: (e) => this.handleFileChange(e), style: { display: "none" }, disabled: this.isInputDisabled(), "aria-label": this.ariaLabelFileInput }), index.h("p", { class: "file-upload-info" }, this.getSupportedFileText()), this.renderStatusMessage()));
    }
    renderDragAndDropArea() {
        const handleInputRef = (el) => {
            this.fileInputEl = el;
        };
        const triggerInputClick = () => {
            if (this.fileInputEl) {
                this.fileInputEl.click();
            }
        };
        return (index.h("div", { class: { disabled: this.isInputDisabled() } }, index.h("div", { class: {
                "upload-dropzone": true,
                "drag-over": this.isDragOver,
                error: this.requiredError,
            }, onClick: triggerInputClick, onDragOver: (e) => this.handleDragOver(e), onDragLeave: (e) => this.handleDragLeave(e), onDrop: (e) => this.handleDrop(e), role: "button", tabIndex: 0, "aria-label": this.ariaLabelDropzone }, index.h("ifx-icon", { icon: "upload-16", class: "custom-icon" }), index.h("p", null, this.labelDragAndDrop), index.h("p", { class: "file-upload-info" }, this.getSupportedFileText()), index.h("div", { style: { height: "0px", overflow: "hidden" } }, index.h("input", { id: this.internalId, ref: handleInputRef, type: "file", accept: this.getAcceptAttribute(), multiple: true, onChange: (e) => this.handleFileChange(e), disabled: this.isInputDisabled(), "aria-label": this.ariaLabelFileInput }))), this.renderStatusMessage()));
    }
};
FileUpload.style = fileUploadCss();

exports.ifx_file_upload = FileUpload;
