const SETTINGS_KEY = "warpbox.upload.settings.v1";

const el = {
    form: document.querySelector("#upload-form"),
    fileInput: document.querySelector("#file-upload"),
    dropSurface: document.querySelector("#drop-surface"),
    dropzone: document.querySelector("#dropzone"),
    fileList: document.querySelector("#file-list"),
    queueLabel: document.querySelector("#queue-label"),
    queueSize: document.querySelector("#queue-size"),
    limitHint: document.querySelector("#limit-hint"),
    boxSpaceText: document.querySelector("#box-space-text"),
    boxSpaceBar: document.querySelector("#box-space-bar"),
    overallBar: document.querySelector("#overall-bar"),
    overallPercent: document.querySelector("#overall-percent"),
    shareLink: document.querySelector("#share-link"),
    copyButton: document.querySelector("#copy-button"),
    startButton: document.querySelector("#start-button"),
    statusText: document.querySelector("#status-text"),
    toast: document.querySelector("#toast"),
    terminal: document.querySelector("#terminal-box"),
    copyCurlButton: document.querySelector("#copy-curl-button"),
    docPopup: document.querySelector("#doc-popup"),
    modalBackdrop: document.querySelector("#modal-backdrop"),
    docPopupTitle: document.querySelector("#doc-popup-title"),
    docPopupBody: document.querySelector("#doc-popup-body"),
    docPopupClose: document.querySelector("#doc-popup-close"),
    expiry: document.querySelector("#expiry-select"),
    password: document.querySelector("#password-input"),
    optionsForm: document.querySelector("#box-options-form"),
    maxViews: document.querySelector("#max-views"),
    boxName: document.querySelector("#box-name"),
    customSlug: document.querySelector("#custom-slug"),
    downloadPage: document.querySelector("#download-page"),
    allowZip: document.querySelector("#allow-zip"),
    allowPreview: document.querySelector("#allow-preview"),
    keepFilenames: document.querySelector("#keep-filenames"),
    privateBox: document.querySelector("#private-box"),
    apiKeyMode: document.querySelector("#api-key-mode"),
    apiKeyInput: document.querySelector("#api-key-input"),
    apiKeyRow: document.querySelector("#api-key-row"),
    apiKeyState: document.querySelector("#api-key-state"),
};

const uploadsEnabled = el.form?.dataset.uploadsEnabled === "true";
const defaultRetention = el.form?.dataset.defaultRetention || "10s";
const maxFileBytes = numberFromDataset(el.form?.dataset.maxFileBytes);
const maxBoxBytes = numberFromDataset(el.form?.dataset.maxBoxBytes);
const oneTimeRetentionKey = "one-time";

let files = [];
let shareUrl = "";
let uploadLocked = false;
let statusTimer = null;
let pendingDuplicateFiles = [];
let apiKeyTimer = null;
let completedImpactKeys = new Set();
let overallImpactDone = false;

function numberFromDataset(value) {
    const number = Number.parseInt(value || "0", 10);
    return Number.isFinite(number) && number > 0 ? number : 0;
}

function formatBytes(bytes) {
    if (!bytes) return "0 B";
    const units = ["B", "KB", "MB", "GB", "TB"];
    let value = bytes;
    let unit = 0;
    while (value >= 1024 && unit < units.length - 1) {
        value /= 1024;
        unit += 1;
    }
    return `${value.toFixed(value >= 10 || unit === 0 ? 0 : 1)} ${units[unit]}`;
}

function htmlEscape(value) {
    return String(value)
        .replaceAll("&", "&amp;")
        .replaceAll("<", "&lt;")
        .replaceAll(">", "&gt;")
        .replaceAll('"', "&quot;")
        .replaceAll("'", "&#39;");
}

function shellQuote(value) {
    return `'${String(value).replaceAll("'", "'\\''")}'`;
}

function totalBytes() {
    return files.reduce((sum, item) => sum + item.file.size, 0);
}

function uploadedBytes() {
    return files.reduce((sum, item) => sum + item.loaded, 0);
}

function overallProgress() {
    const total = totalBytes();
    return total ? Math.round((uploadedBytes() / total) * 100) : 0;
}

function oversizedFiles() {
    return maxFileBytes ? files.filter((item) => item.file.size > maxFileBytes) : [];
}

function isOverBoxQuota() {
    return maxBoxBytes ? totalBytes() > maxBoxBytes : false;
}

function hasQuotaError() {
    return isOverBoxQuota() || oversizedFiles().length > 0;
}

function normalizedFileName(name) {
    return String(name || "").trim().toLowerCase();
}

function splitNameForIncrement(name) {
    const value = String(name || "file");
    const dot = value.lastIndexOf(".");
    if (dot > 0 && dot < value.length - 1) return [value.slice(0, dot), value.slice(dot)];
    return [value, ""];
}

function nextIncrementedFileName(name, usedNames) {
    const [base, ext] = splitNameForIncrement(name);
    let index = 2;
    let candidate = `${base} (${index})${ext}`;
    while (usedNames.has(normalizedFileName(candidate))) {
        index += 1;
        candidate = `${base} (${index})${ext}`;
    }
    usedNames.add(normalizedFileName(candidate));
    return candidate;
}

function makeQueuedFile(file, displayName = file.name) {
    return {
        file,
        displayName,
        loaded: 0,
        uploaded: false,
        failed: false,
        error: "",
        row: null,
        boxID: "",
        boxFile: null,
        previewURL: file.type?.startsWith("image/") ? URL.createObjectURL(file) : "",
    };
}

function iconForFile(file) {
    const filename = file.name || "";
    const mimeType = file.type || "";
    const extension = filename.includes(".") ? filename.slice(filename.lastIndexOf(".")).toLowerCase() : "";

    if (extension === ".exe") return "/static/img/icons/Program Files Icons - PNG/MSONSEXT.DLL_14_6-0.png";
    if (mimeType.startsWith("image/")) return "/static/img/sprites/bitmap.png";
    if (mimeType.startsWith("video/") || mimeType.startsWith("audio/")) return "/static/img/icons/netshow_notransm-1.png";
    if (mimeType.startsWith("text/") || extension === ".md") return "/static/img/sprites/notepad_file-1.png";
    if (mimeType.includes("zip") || mimeType.includes("compressed") || [".rar", ".7z", ".tar", ".gz"].includes(extension)) return "/static/img/icons/Windows Icons - PNG/zipfldr.dll_14_101-0.png";
    if ([".ttf", ".otf", ".woff", ".woff2"].includes(extension)) return "/static/img/sprites/font.png";
    if (extension === ".pdf") return "/static/img/sprites/journal.png";
    if ([".html", ".css", ".js"].includes(extension)) return "/static/img/sprites/frame_web-0.png";
    return "/static/img/icons/Windows Icons - PNG/ole2.dll_14_DEFICON.png";
}

function setStatus(message) {
    if (el.statusText) el.statusText.textContent = message;
}

function showToast(message, type = "info") {
    window.WarpBoxUI.toast(message, type, { target: el.toast });
}

function closeMenus() {
    document.querySelectorAll(".menu-item.is-open").forEach((node) => {
        node.classList.remove("is-open");
        node.querySelector(".menu-button")?.setAttribute("aria-expanded", "false");
    });
}

function disabledReasonFor(target) {
    const control = target.closest("[data-disabled-reason], button, input, select, textarea, .upload-dropzone, .option-check, .option-row");
    if (!control) return "";
    if (control.classList.contains("option-check") || control.classList.contains("option-row")) {
        const nested = control.querySelector("input, select, textarea");
        if (nested?.disabled || nested?.readOnly || nested?.getAttribute("aria-disabled") === "true") {
            return nested.dataset.disabledReason || "This option is disabled right now.";
        }
    }
    if (control.classList.contains("upload-dropzone") && uploadLocked) {
        return control.dataset.disabledReason || "The current box is sealed after upload. Press Clear to start a new box.";
    }
    if (control.disabled || control.readOnly || control.getAttribute("aria-disabled") === "true") {
        return control.dataset.disabledReason || control.title || "This control is disabled right now.";
    }
    return "";
}

function announceDisabledReason(event) {
    const reason = disabledReasonFor(event.target);
    if (!reason) return false;
    event.preventDefault();
    event.stopPropagation();
    closeMenus();
    showToast(reason, "warning");
    setStatus(reason);
    return true;
}

function stopStatusAnimation() {
    if (statusTimer) {
        clearInterval(statusTimer);
        statusTimer = null;
    }
}

function animateUploadStatus(getPrefix) {
    let dotCount = 0;
    stopStatusAnimation();
    statusTimer = setInterval(() => {
        dotCount = (dotCount % 3) + 1;
        setStatus(`${getPrefix()} Uploading${".".repeat(dotCount)}`);
    }, 350);
}

function setShareUrl(url) {
    shareUrl = url ? new URL(url, window.location.origin).toString() : "";
    if (!el.shareLink || !el.copyButton) return;
    el.shareLink.textContent = shareUrl || "Not created yet";
    el.shareLink.href = shareUrl || "#";
    el.shareLink.title = shareUrl;
    el.shareLink.classList.toggle("is-empty", !shareUrl);
    el.shareLink.setAttribute("aria-disabled", shareUrl ? "false" : "true");
    el.copyButton.disabled = false;
    el.copyButton.setAttribute("aria-disabled", shareUrl ? "false" : "true");
    el.copyButton.dataset.disabledReason = shareUrl ? "" : "There is no share URL yet. Start an upload first.";
    updateDisabledReasons();
    updateTerminal();
    updateCurrentStep();
}

function setOverallProgress(percent) {
    const clamped = Math.max(0, Math.min(100, percent));
    const display = `${Math.round(clamped)}%`;
    if (el.overallBar) el.overallBar.style.width = display;
    if (el.overallPercent) el.overallPercent.textContent = display;
}

function flashProgressBar(bar) {
    if (!bar) return;
    bar.classList.remove("just-completed");
    void bar.offsetWidth;
    bar.classList.add("just-completed");
    setTimeout(() => bar.classList.remove("just-completed"), 620);
}

function setRowProgress(item, percent) {
    const bar = item.row?.querySelector(".upload-progress-bar");
    if (bar) bar.style.width = `${Math.max(0, Math.min(100, percent))}%`;
}

function updateCurrentStep() {
    const hasFiles = files.length > 0;
    const allDone = hasFiles && files.every((item) => item.uploaded);
    el.dropzone?.classList.toggle("is-current-step", uploadsEnabled && !hasFiles && !uploadLocked);
    el.startButton?.classList.toggle("is-current-step", uploadsEnabled && hasFiles && !allDone && !uploadLocked && !hasQuotaError());
    document.querySelector(".upload-result")?.classList.toggle("is-current-step", allDone && Boolean(shareUrl));
}

function quotaWarningMessage(incoming = []) {
    const combined = [...files, ...incoming];
    const tooBig = maxFileBytes ? combined.filter((item) => item.file.size > maxFileBytes) : [];
    const total = combined.reduce((sum, item) => sum + item.file.size, 0);
    if (tooBig.length) {
        const list = tooBig.slice(0, 4).map((item) => `${item.displayName} (${formatBytes(item.file.size)})`).join(", ");
        const more = tooBig.length > 4 ? ` and ${tooBig.length - 4} more` : "";
        return `These files are over the single-file limit of ${formatBytes(maxFileBytes)}: ${list}${more}. Remove them before uploading.`;
    }
    if (maxBoxBytes && total > maxBoxBytes) {
        return `This box is ${formatBytes(total - maxBoxBytes)} over the ${formatBytes(maxBoxBytes)} limit. Remove some files before uploading.`;
    }
    return "";
}

function updateLimitHint() {
    if (!el.limitHint) return;
    const parts = [];
    if (maxBoxBytes) parts.push(`Max box: ${formatBytes(maxBoxBytes)}`);
    if (maxFileBytes) parts.push(`max file: ${formatBytes(maxFileBytes)}`);
    parts.push("links expire automatically");
    el.limitHint.textContent = parts.join(" · ");
}

function updateQuota() {
    const used = totalBytes();
    const limitText = maxBoxBytes ? ` / ${formatBytes(maxBoxBytes)}` : "";
    const overQuota = isOverBoxQuota();
    const overFile = oversizedFiles().length > 0;
    const percent = maxBoxBytes ? Math.min(100, Math.round((used / maxBoxBytes) * 100)) : 0;
    document.querySelector(".upload-quota")?.classList.toggle("is-quota-warning", overQuota || overFile);
    if (el.boxSpaceText) el.boxSpaceText.textContent = `${formatBytes(used)}${limitText}${overQuota ? " - over quota" : ""}`;
    if (el.boxSpaceBar) {
        el.boxSpaceBar.style.width = `${percent}%`;
        el.boxSpaceBar.classList.toggle("is-over-quota", overQuota || overFile);
    }
}

function updateQueueSummary() {
    const count = files.length;
    if (el.queueLabel) el.queueLabel.textContent = count ? `${count} file${count === 1 ? "" : "s"} selected` : "No files selected";
    if (el.queueSize) el.queueSize.textContent = `${formatBytes(totalBytes())} total`;
}

function updateOverallProgress() {
    const uploadedCount = files.filter((item) => item.uploaded).length;
    const percent = overallProgress();
    setOverallProgress(percent >= 100 && uploadedCount < files.length ? 99 : percent);
    if (percent >= 100 && files.length && !overallImpactDone) {
        overallImpactDone = true;
        flashProgressBar(el.overallBar);
    }
}

function createFileRow(item, index) {
    const row = document.createElement("div");
    row.className = "upload-file-row";
    row.dataset.index = String(index);
    row.classList.toggle("has-thumbnail", Boolean(item.previewURL));
    row.classList.toggle("is-too-large", maxFileBytes > 0 && item.file.size > maxFileBytes);
    row.classList.toggle("is-working", item.loaded > 0 && !item.uploaded && !item.failed);
    row.classList.toggle("is-uploaded", item.uploaded);
    row.classList.toggle("is-failed", item.failed);
    row.title = item.error || "";

    const icon = document.createElement("img");
    icon.className = "upload-file-icon";
    icon.src = item.previewURL || iconForFile(item.file);
    icon.alt = "";
    icon.setAttribute("aria-hidden", "true");

    const name = document.createElement("span");
    name.className = "upload-file-name";
    name.textContent = item.displayName;
    name.title = item.displayName;

    const size = document.createElement("span");
    size.className = "upload-file-size";
    size.textContent = formatBytes(item.file.size);

    const remove = document.createElement("button");
    remove.className = "win98-button upload-file-remove";
    remove.type = "button";
    remove.textContent = "×";
    remove.dataset.remove = String(index);
    remove.title = uploadLocked ? "This file cannot be removed because this upload box was already created." : "Remove file";
    remove.disabled = false;
    remove.setAttribute("aria-disabled", uploadLocked ? "true" : "false");
    remove.dataset.disabledReason = uploadLocked ? "Files cannot be removed after the box is created. Press Clear to start another upload." : "";

    const progress = document.createElement("span");
    progress.className = "upload-progress";
    progress.setAttribute("aria-label", `Upload progress ${Math.round(item.file.size ? (item.loaded / item.file.size) * 100 : 0)} percent`);

    const progressBar = document.createElement("span");
    progressBar.className = "upload-progress-bar";
    progressBar.style.width = `${item.uploaded ? 100 : item.failed ? 100 : Math.max(0, Math.min(100, item.file.size ? (item.loaded / item.file.size) * 100 : 0))}%`;
    progress.append(progressBar);

    row.append(icon, name, size, remove, progress);
    item.row = row;
    return row;
}

function renderFiles() {
    if (!el.fileList) return;
    el.fileList.replaceChildren();

    if (!files.length) {
        const empty = document.createElement("p");
        empty.className = "upload-empty-state";
        empty.textContent = uploadsEnabled
            ? "No files in the box yet. Drop files here, use File > Add files, or click the dropzone."
            : "Guest uploads are disabled.";
        el.fileList.append(empty);
    } else {
        const fragment = document.createDocumentFragment();
        files.forEach((item, index) => fragment.append(createFileRow(item, index)));
        el.fileList.append(fragment);
    }

    updateQueueSummary();
    updateQuota();
    updateOverallProgress();
    updateTerminal();
    updateDisabledReasons();
    updateCurrentStep();
}

function duplicateFileReport(incoming = []) {
    const used = new Set(files.map((item) => normalizedFileName(item.displayName)));
    const duplicates = [];
    const unique = [];
    incoming.forEach((item) => {
        const key = normalizedFileName(item.displayName);
        if (used.has(key)) {
            duplicates.push(item);
            return;
        }
        used.add(key);
        unique.push(item);
    });
    return { unique, duplicates };
}

function addFiles(fileList) {
    if (!uploadsEnabled) {
        showToast("Guest uploads are disabled.", "warning");
        return;
    }
    if (uploadLocked) {
        showToast("This box is sealed. Clear it to create a fresh upload.", "warning");
        return;
    }
    const incoming = Array.from(fileList || []).map((file) => makeQueuedFile(file));
    if (!incoming.length) return;

    const { unique, duplicates } = duplicateFileReport(incoming);
    if (unique.length) {
        files.push(...unique);
        setShareUrl("");
        renderFiles();
        const warning = quotaWarningMessage();
        if (warning) showWarningDialog("Quota warning", warning);
    }
    if (duplicates.length) showDuplicateDialog(duplicates);

    if (unique.length) setStatus(`${unique.length} file${unique.length === 1 ? "" : "s"} added to queue`);
    if (duplicates.length && !unique.length) setStatus(`${duplicates.length} duplicate file${duplicates.length === 1 ? "" : "s"} need your choice`);
}

function showDuplicateDialog(duplicates) {
    pendingDuplicateFiles = duplicates;
    const list = duplicates.map((item) => `<li><strong>${htmlEscape(item.displayName)}</strong> <span>${formatBytes(item.file.size)}</span></li>`).join("");
    showTemplatePopup("Duplicate file names", "duplicate", { list })
        .then(() => document.querySelector("#duplicate-append")?.focus());
    showToast("Duplicate names found. Choose skip or append numbers.", "warning");
}

function appendPendingDuplicates() {
    if (!pendingDuplicateFiles.length) return;
    const used = new Set(files.map((item) => normalizedFileName(item.displayName)));
    pendingDuplicateFiles.forEach((item) => {
        item.displayName = nextIncrementedFileName(item.displayName, used);
        files.push(item);
    });
    const count = pendingDuplicateFiles.length;
    pendingDuplicateFiles = [];
    closeDoc();
    setShareUrl("");
    renderFiles();
    showToast("Duplicate files added with numbered names.", "info");
    setStatus(`${count} duplicate file${count === 1 ? "" : "s"} added with numbered names`);
}

function removeFile(index) {
    if (uploadLocked) {
        showToast("Box already created. Clear it before editing the queue.", "warning");
        return;
    }
    const [removed] = files.splice(index, 1);
    if (removed?.previewURL) URL.revokeObjectURL(removed.previewURL);
    setShareUrl("");
    renderFiles();
    setStatus("File removed from queue");
}

function clearQueue() {
    files.forEach((item) => {
        if (item.previewURL) URL.revokeObjectURL(item.previewURL);
    });
    files = [];
    pendingDuplicateFiles = [];
    uploadLocked = false;
    completedImpactKeys = new Set();
    overallImpactDone = false;
    stopStatusAnimation();
    setBoxOptionsLocked(false);
    setShareUrl("");
    if (el.fileInput) {
        el.fileInput.value = "";
        el.fileInput.disabled = !uploadsEnabled;
    }
    el.dropzone?.classList.remove("is-locked");
    renderFiles();
    setStatus(uploadsEnabled ? "Queue cleared" : "Guest uploads are disabled");
    showToast("Queue cleared.");
}

function confirmClearQueue() {
    if (!files.length && !shareUrl) {
        showToast("Nothing to clear.");
        return;
    }
    showTemplatePopup("Clear WarpBox?", "clear")
        .then(() => document.querySelector("#confirm-clear-no")?.focus());
}

async function createBox() {
    const response = await fetch("/box", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
            retention_key: el.expiry?.value || defaultRetention,
            password: el.password?.value || "",
            allow_zip: isOneTimeDownloadSelected() || !el.allowZip || el.allowZip.checked,
            files: files.map((item) => ({ name: item.displayName, size: item.file.size })),
        }),
    });

    const result = await readJSON(response);
    if (!response.ok) throw new Error(result.error || "Could not create upload box");
    return result;
}

async function readJSON(response) {
    try {
        return await response.json();
    } catch (_) {
        return {};
    }
}

async function markFileStatus(item, status) {
    if (!item.boxID || !item.boxFile) return;
    try {
        await fetch(`/box/${item.boxID}/files/${item.boxFile.id}/status`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ status }),
        });
    } catch (_) {
        // Best effort only. The upload endpoint also marks hard failures.
    }
}

function setFileFailed(item, message) {
    item.failed = true;
    item.uploaded = false;
    item.error = message || "Failed to upload";
    item.loaded = item.file.size;
    item.row?.classList.remove("is-working", "is-uploaded");
    item.row?.classList.add("is-failed");
    if (item.row) item.row.title = item.error;
    setRowProgress(item, 100);
    updateOverallProgress();
}

function markCompletedImpact(item) {
    const key = item.boxFile?.id || item.displayName;
    if (completedImpactKeys.has(key)) return;
    completedImpactKeys.add(key);
    flashProgressBar(item.row?.querySelector(".upload-progress-bar"));
}

function uploadFile(item, onComplete) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        const formData = new FormData();
        formData.append("file", item.file, item.displayName);

        xhr.open("POST", item.boxFile.upload_path);

        xhr.upload.addEventListener("loadstart", () => {
            item.loaded = 0;
            item.failed = false;
            item.uploaded = false;
            item.row?.classList.remove("is-failed", "is-uploaded");
            item.row?.classList.add("is-working");
            setRowProgress(item, 2);
            updateOverallProgress();
        });

        xhr.upload.addEventListener("progress", (event) => {
            if (!event.lengthComputable) return;
            item.loaded = Math.min(event.loaded, item.file.size);
            const percent = (event.loaded / event.total) * 100;
            setRowProgress(item, percent >= 100 ? 99 : percent);
            updateOverallProgress();
        });

        xhr.addEventListener("load", async () => {
            if (xhr.status < 200 || xhr.status >= 300) {
                let message = "Upload failed";
                try {
                    message = JSON.parse(xhr.responseText).error || message;
                } catch (_) {}
                setFileFailed(item, message);
                await markFileStatus(item, "failed");
                reject(new Error(message));
                return;
            }

            item.uploaded = true;
            item.failed = false;
            item.loaded = item.file.size;
            item.row?.classList.remove("is-working", "is-failed");
            item.row?.classList.add("is-uploaded");
            if (item.row) item.row.title = "Uploaded";
            setRowProgress(item, 100);
            markCompletedImpact(item);

            try {
                const result = JSON.parse(xhr.responseText);
                if (result.file) {
                    item.boxFile = result.file;
                    const icon = item.row?.querySelector(".upload-file-icon");
                    if (icon && result.file.thumbnail_path) {
                        item.row.classList.add("has-thumbnail");
                        icon.src = result.file.thumbnail_path;
                    } else if (icon && result.file.icon_path && !item.previewURL) {
                        icon.src = result.file.icon_path;
                    }
                }
            } catch (_) {}

            updateOverallProgress();
            onComplete();
            resolve();
        });

        xhr.addEventListener("error", async () => {
            setFileFailed(item, "Network error while uploading");
            await markFileStatus(item, "failed");
            reject(new Error("Network error while uploading"));
        });

        xhr.addEventListener("abort", async () => {
            setFileFailed(item, "Upload cancelled");
            await markFileStatus(item, "failed");
            reject(new Error("Upload cancelled"));
        });

        markFileStatus(item, "uploading");
        xhr.send(formData);
    });
}

async function startUpload() {
    if (!uploadsEnabled) {
        showToast("Guest uploads are disabled.", "warning");
        return;
    }
    if (uploadLocked) {
        showToast("Upload already started. Press Clear to create another box.", "warning");
        return;
    }
    if (!files.length) {
        showWarningDialog("No files selected", "There are no files selected. Please select files to upload.");
        showToast("No files selected. Please select files to upload.", "warning");
        setStatus("No files selected");
        return;
    }
    if (hasQuotaError()) {
        showWarningDialog("Over maximum upload size", quotaWarningMessage() || "Over maximum upload size.");
        showToast("Over maximum upload size.", "error");
        return;
    }

    uploadLocked = true;
    setBoxOptionsLocked(true);
    if (el.fileInput) el.fileInput.disabled = true;
    el.dropzone?.classList.add("is-locked");
    setShareUrl("");
    files.forEach((item) => {
        item.loaded = 0;
        item.uploaded = false;
        item.failed = false;
        item.error = "";
    });
    completedImpactKeys = new Set();
    overallImpactDone = false;
    renderFiles();

    let completedCount = 0;
    const totalCount = files.length;
    const statusPrefix = () => `${completedCount}/${totalCount}`;
    setStatus(`${statusPrefix()} Uploading.`);
    animateUploadStatus(statusPrefix);

    try {
        const box = await createBox();
        setShareUrl(box.box_url);
        files.forEach((item, index) => {
            item.boxID = box.box_id;
            item.boxFile = box.files[index];
            item.displayName = item.boxFile?.name || item.displayName;
            const icon = item.row?.querySelector(".upload-file-icon");
            if (icon && item.boxFile?.thumbnail_path) {
                item.row.classList.add("has-thumbnail");
                icon.src = item.boxFile.thumbnail_path;
            } else if (icon && item.boxFile?.icon_path && !item.previewURL) {
                icon.src = item.boxFile.icon_path;
            }
        });

        const results = await Promise.allSettled(files.map((item) => uploadFile(item, () => { completedCount += 1; })));
        stopStatusAnimation();

        const failedCount = results.filter((result) => result.status === "rejected").length;
        if (failedCount > 0) {
            setStatus(`${completedCount}/${totalCount} uploaded, ${failedCount} failed`);
            showToast(`${failedCount} file${failedCount === 1 ? "" : "s"} failed. The share URL contains the successful files.`, "error");
            renderFiles();
            return;
        }

        setOverallProgress(100);
        setStatus(`${completedCount}/${totalCount} uploaded. Share URL created. Press Clear to start another upload.`);
        showToast("Upload complete. Share URL created.");
        renderFiles();
    } catch (error) {
        stopStatusAnimation();
        uploadLocked = false;
        setBoxOptionsLocked(false);
        if (el.fileInput) el.fileInput.disabled = !uploadsEnabled;
        el.dropzone?.classList.remove("is-locked");
        setShareUrl("");
        setStatus(error.message || "Upload failed");
        showToast(error.message || "Upload failed", "error");
        renderFiles();
    }
}

function isOneTimeDownloadSelected() {
    return el.expiry?.value === oneTimeRetentionKey;
}

function syncZipForRetention() {
    if (!el.allowZip) return;
    if (isOneTimeDownloadSelected()) {
        el.allowZip.checked = true;
        el.allowZip.disabled = true;
    } else if (!uploadLocked) {
        el.allowZip.disabled = false;
    }
}

function setBoxOptionsLocked(locked) {
    const controls = [el.expiry, el.password, el.maxViews, el.boxName, el.customSlug, el.downloadPage, el.allowZip, el.allowPreview, el.keepFilenames, el.privateBox, el.apiKeyMode, el.apiKeyInput].filter(Boolean);
    el.optionsForm?.classList.toggle("is-locked", locked);
    controls.forEach((control) => {
        control.dataset.disabledReason = locked ? "Box Options are locked because this box was already created. Press Clear to start another upload." : "";
        if (control.tagName === "INPUT" && !["checkbox", "radio", "file"].includes(control.type)) {
            control.readOnly = locked;
        } else {
            control.disabled = locked;
        }
    });
    if (el.password) el.password.type = locked ? "password" : "text";
    if (!locked) {
        syncZipForRetention();
        syncApiKeyField();
    }
    updateDisabledReasons();
}

function updateDisabledReasons() {
    if (el.startButton) {
        let reason = "";
        if (!uploadsEnabled) reason = "Guest uploads are disabled.";
        else if (uploadLocked) reason = "This upload already started. Press Clear to create another box.";
        else if (hasQuotaError()) reason = "Over maximum upload size. Remove highlighted files or clear some files.";
        else if (!files.length) reason = "There are no files selected. Please select files to upload.";
        el.startButton.disabled = false;
        el.startButton.setAttribute("aria-disabled", reason ? "true" : "false");
        el.startButton.dataset.disabledReason = reason;
        el.startButton.title = reason;
    }
    if (el.fileInput) {
        el.fileInput.dataset.disabledReason = uploadLocked ? "The current box is sealed after upload. Press Clear to start a new box." : (!uploadsEnabled ? "Guest uploads are disabled." : "");
    }
    if (el.dropzone) {
        el.dropzone.dataset.disabledReason = uploadLocked ? "The current box is sealed after upload. Press Clear to start a new box." : (!uploadsEnabled ? "Guest uploads are disabled." : "");
    }
    document.querySelectorAll('[data-action="start-upload"]').forEach((button) => {
        const reason = el.startButton?.dataset.disabledReason || "";
        button.setAttribute("aria-disabled", reason ? "true" : "false");
        button.dataset.disabledReason = reason;
    });
    document.querySelectorAll('[data-action="browse"]').forEach((button) => {
        const reason = uploadLocked ? "The current box is sealed after upload. Press Clear to start a new box." : (!uploadsEnabled ? "Guest uploads are disabled." : "");
        button.setAttribute("aria-disabled", reason ? "true" : "false");
        button.dataset.disabledReason = reason;
    });
    document.querySelectorAll('[data-action="copy-link"]').forEach((button) => {
        button.setAttribute("aria-disabled", shareUrl ? "false" : "true");
        button.dataset.disabledReason = shareUrl ? "" : "There is no share URL yet. Start an upload first.";
    });
}

function saveSettings() {
    const apiKey = el.apiKeyMode?.checked && validApiKey(el.apiKeyInput?.value || "") ? el.apiKeyInput.value.trim() : "";
    const settings = {
        maxViews: el.maxViews?.value || "",
        allowPreview: Boolean(el.allowPreview?.checked),
        keepFilenames: Boolean(el.keepFilenames?.checked),
        privateBox: Boolean(el.privateBox?.checked),
        apiKeyMode: Boolean(el.apiKeyMode?.checked),
        apiKey,
    };
    localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
}

function loadSettings() {
    let settings = {};
    try {
        settings = JSON.parse(localStorage.getItem(SETTINGS_KEY) || "{}");
    } catch (_) {}
    if (el.maxViews) el.maxViews.value = settings.maxViews || "";
    if (el.allowPreview) el.allowPreview.checked = settings.allowPreview !== false;
    if (el.keepFilenames) el.keepFilenames.checked = settings.keepFilenames !== false;
    if (el.privateBox) el.privateBox.checked = Boolean(settings.privateBox);
    if (el.apiKeyMode) el.apiKeyMode.checked = Boolean(settings.apiKeyMode);
    if (el.apiKeyInput) el.apiKeyInput.value = validApiKey(settings.apiKey || "") ? settings.apiKey : "";
    syncZipForRetention();
    syncApiKeyField();
    saveSettings();
}

function syncMenuChecks() {
    updateDisabledReasons();
}

function syncApiKeyField() {
    const enabled = Boolean(el.apiKeyMode?.checked) && !uploadLocked;
    el.apiKeyRow?.classList.toggle("is-visible", Boolean(el.apiKeyMode?.checked));
    if (el.apiKeyInput) {
        el.apiKeyInput.disabled = !enabled;
        el.apiKeyInput.dataset.disabledReason = enabled ? "" : "Enable Use API key for larger quota before typing an API key.";
    }
    validateApiKeyField();
}

function validateApiKeyField() {
    if (!el.apiKeyInput || !el.apiKeyState) return;
    clearTimeout(apiKeyTimer);
    const wrapper = el.apiKeyInput.closest(".api-key-field");
    wrapper?.classList.remove("is-checking");

    if (!el.apiKeyMode?.checked) {
        el.apiKeyState.textContent = "";
        return;
    }
    const value = el.apiKeyInput.value.trim();
    if (!value) {
        el.apiKeyState.textContent = "waiting";
        saveSettings();
        return;
    }

    el.apiKeyInput.disabled = true;
    wrapper?.classList.add("is-checking");
    el.apiKeyState.textContent = "checking";
    apiKeyTimer = setTimeout(() => {
        wrapper?.classList.remove("is-checking");
        el.apiKeyInput.disabled = uploadLocked;
        if (validApiKey(value)) {
            el.apiKeyState.textContent = "saved locally";
            saveSettings();
        } else {
            el.apiKeyInput.value = "";
            el.apiKeyState.textContent = "invalid";
            saveSettings();
            showToast("Invalid API key removed. Paste a valid API key to save it.", "warning");
        }
    }, 650);
}

function validApiKey(value) {
    return /^[A-Za-z0-9._-]{12,}$/.test(String(value || "").trim());
}

function slugify(value) {
    return String(value || "")
        .toLowerCase()
        .replace(/[^a-z0-9-]+/g, "-")
        .replace(/-+/g, "-")
        .replace(/^-|-$/g, "")
        .slice(0, 32);
}

function sanitizeSlugInput(value) {
    return String(value || "")
        .toLowerCase()
        .replace(/[^a-z0-9-]/g, "")
        .replace(/-+/g, "-")
        .slice(0, 32);
}

function syncSlugFromName(force = false) {
    if (!el.customSlug || !el.boxName) return;
    if (force || !el.customSlug.value || el.customSlug.dataset.auto === "true") {
        el.customSlug.value = slugify(el.boxName.value);
        el.customSlug.dataset.auto = "true";
    }
    saveSettings();
    updateTerminal();
}

function randomPassword() {
    if (!el.password || uploadLocked) return;
    el.password.value = `${Math.random().toString(36).slice(2, 8)}-${Math.random().toString(36).slice(2, 6)}`;
    saveSettings();
    updateTerminal();
    setStatus("Generated a password");
}

function randomBoxName() {
    if (!el.boxName || uploadLocked) return;
    const adjectives = ["Neon", "Turbo", "Quiet", "Cosmic", "Lucky", "Midnight", "Pixel", "Rapid"];
    const nouns = ["Floppy Disk", "Archive Box", "Packet Portal", "Upload Folder", "Cache Drive", "Release Bundle"];
    el.boxName.value = `${adjectives[Math.floor(Math.random() * adjectives.length)]} ${nouns[Math.floor(Math.random() * nouns.length)]}`;
    syncSlugFromName(true);
    setStatus("Generated a local box name");
}

function getCurlCommand({ full = true } = {}) {
    const args = [];
    const selectedFiles = files.length ? files : [{ displayName: "build.zip" }];
    const previewLimit = full ? selectedFiles.length : 4;
    selectedFiles.slice(0, previewLimit).forEach((item) => args.push(`  -F ${shellQuote(`files=@${item.displayName}`)}`));
    const hiddenFileCount = !full && selectedFiles.length > previewLimit ? selectedFiles.length - previewLimit : 0;
    args.push(`  -F ${shellQuote(`retention=${el.expiry?.value || defaultRetention}`)}`);
    if (el.password?.value) args.push(`  -F ${shellQuote("password=YOUR_PASSWORD")}`);
    if (el.allowZip && !el.allowZip.checked) args.push(`  -F ${shellQuote("allow_zip=false")}`);

    const commandLines = ["curl"];
    if (el.apiKeyMode?.checked) commandLines.push(`  -H ${shellQuote("Authorization: Bearer YOUR_API_KEY")}`);
    commandLines.push(...args, `  ${window.location.origin}/upload`);
    const command = commandLines.join(" \\\n");
    return hiddenFileCount ? `${command}\n# and ${hiddenFileCount} other files included when copying` : command;
}

function updateTerminal() {
    if (!el.terminal) return;
    const command = getCurlCommand({ full: false });
    el.terminal.innerHTML = `<span class="terminal-muted">warpbox@cli</span>:~$ ${htmlEscape(command)}`;
}

async function copyText(kind, value, openUrl = "") {
    if (!value) {
        showToast(`No ${kind.toLowerCase()} yet.`, "warning");
        return;
    }
    try {
        await navigator.clipboard.writeText(value);
        showToast(`${kind} copied to clipboard.`);
        setStatus(`Copied ${kind.toLowerCase()}`);
    } catch (_) {
        showCopyFallback(kind, value, openUrl);
    }
}

function showCopyFallback(kind, value, openUrl) {
    const openLink = openUrl ? `<a class="win98-button" href="${htmlEscape(openUrl)}" target="_blank" rel="noreferrer">Open</a>` : "";
    showTemplatePopup(`${kind} copy failed`, "copy-failed", {
        value: htmlEscape(value),
        openLink,
    });
}

function quotaWarningHtml(message) {
    const tooLarge = oversizedFiles();
    const parts = [];
    if (tooLarge.length) {
        parts.push("<p class=\"quota-dialog-summary\"><strong>Single-file limit exceeded.</strong> Remove these files before uploading.</p>");
        parts.push(`<ol class="quota-dialog-list">${tooLarge.map((item) => `<li><strong>${htmlEscape(item.displayName)}</strong> <span>${formatBytes(item.file.size)} / max ${formatBytes(maxFileBytes)}</span></li>`).join("")}</ol>`);
    }
    if (isOverBoxQuota()) {
        parts.push(`<p class="quota-dialog-summary"><strong>Box quota exceeded.</strong> Current total is ${formatBytes(totalBytes())}. The limit is ${formatBytes(maxBoxBytes)}. Remove ${formatBytes(totalBytes() - maxBoxBytes)} or more.</p>`);
    }
    if (!parts.length) parts.push(`<p>${htmlEscape(message)}</p>`);
    return parts.join("");
}

function showWarningDialog(title, message) {
    showTemplatePopup(title, "warning", {
        title: htmlEscape(title),
        content: quotaWarningHtml(message),
    });
}

function openPopup(title, html, about = false) {
    window.WarpBoxUI.openPopup(title, html, {
        about,
        popup: el.docPopup,
        title: el.docPopupTitle,
        body: el.docPopupBody,
        backdrop: el.modalBackdrop,
    });
}

function closeDoc() {
    window.WarpBoxUI.closePopup({ popup: el.docPopup, backdrop: el.modalBackdrop });
}

async function showTemplatePopup(title, templateName, data = {}, about = false) {
    try {
        const html = await window.WBPopups.renderTemplate(templateName, data);
        openPopup(title, html, about);
    } catch (error) {
        showToast(error.message || `Could not load ${title}.`, "error");
    }
}

function popupTemplateData(name) {
    const data = { origin: window.location.origin };
    if (name !== "dailyQuota") return data;
    return {
        ...data,
        boxLimit: maxBoxBytes ? formatBytes(maxBoxBytes) : "No configured limit",
        boxPercent: maxBoxBytes ? Math.min(100, Math.round((totalBytes() / maxBoxBytes) * 100)) : 0,
        fileLimit: maxFileBytes ? formatBytes(maxFileBytes) : "No configured limit",
        filePercent: oversizedFiles().length ? 100 : 0,
    };
}

async function openDoc(name) {
    try {
        const doc = await window.WBPopups.renderDoc(name, popupTemplateData(name));
        if (!doc) return;
        openPopup(doc.title, doc.html, doc.about);
        setStatus(`${doc.title} opened`);
    } catch (error) {
        showToast(error.message || "Could not load help window.", "error");
    }
}

document.addEventListener("click", (event) => {
    if (announceDisabledReason(event)) return;

    const menuButton = event.target.closest(".menu-button");
    if (menuButton) {
        const item = menuButton.closest(".menu-item");
        const isOpen = item.classList.contains("is-open");
        closeMenus();
        item.classList.toggle("is-open", !isOpen);
        menuButton.setAttribute("aria-expanded", String(!isOpen));
        return;
    }

    const action = event.target.closest("[data-action]")?.dataset.action;
    if (action) {
        closeMenus();
        if (action === "browse") el.fileInput?.click();
        if (action === "start-upload") startUpload();
        if (action === "copy-link") copyText("Share URL", shareUrl, shareUrl);
        if (action === "clear") confirmClearQueue();
        if (action === "toggle-delete-once" && el.expiry?.querySelector(`option[value="${oneTimeRetentionKey}"]`)) {
            el.expiry.value = isOneTimeDownloadSelected() ? defaultRetention : oneTimeRetentionKey;
            syncZipForRetention();
            saveSettings();
            syncMenuChecks();
            updateTerminal();
        }
        if (action === "random-password") randomPassword();
        if (action === "random-box-name") randomBoxName();
        if (action === "clear-password" && el.password && !uploadLocked) {
            el.password.value = "";
            saveSettings();
            updateTerminal();
        }
        if (action === "toggle-page" && el.downloadPage && !uploadLocked) {
            el.downloadPage.checked = !el.downloadPage.checked;
            saveSettings();
            syncMenuChecks();
        }
        if (action === "help" || action === "side-help") openDoc("faq");
        if (action === "coming-soon") showToast("Coming Soon, not implemented just yet.");
        if (action === "fake-close") showToast("Close button denied. The upload window is staying open.", "warning");
        if (action === "minimize") showToast("Minimize requested. WarpBox stays visible so your queue is safe.");
        if (action === "toggle-fit") {
            document.body.classList.toggle("fit-window");
            showToast("Maximize requested. The pixel rectangle feels important now.");
        }
        if (action === "side-close") showToast("Box Options refuses to leave. Settings stay visible.");
        if (action === "side-help") showToast("Terminal help opened. Copy the command and feed it files.");
        if (action === "side-folder-close") showToast("The folder window saw that click and chose denial.");
        return;
    }

    const doc = event.target.closest("[data-doc]")?.dataset.doc;
    if (doc) {
        openDoc(doc);
        return;
    }

    const remove = event.target.closest("[data-remove]");
    if (remove) {
        removeFile(Number(remove.dataset.remove));
        return;
    }

    if (event.target.id === "duplicate-append") appendPendingDuplicates();
    if (event.target.id === "duplicate-skip") {
        pendingDuplicateFiles = [];
        closeDoc();
        showToast("Duplicate files skipped.");
    }
    if (event.target.id === "confirm-clear-yes") {
        closeDoc();
        clearQueue();
    }
    if (event.target.id === "confirm-clear-no" || event.target.id === "fallback-close") closeDoc();

    if (!event.target.closest(".menu-item")) {
        closeMenus();
    }
});

document.addEventListener("mousedown", (event) => {
    announceDisabledReason(event);
}, true);

document.querySelectorAll(".menu-item").forEach((item) => {
    item.addEventListener("mouseenter", () => {
        if (!document.querySelector(".menu-item.is-open")) return;
        closeMenus();
        item.classList.add("is-open");
        item.querySelector(".menu-button")?.setAttribute("aria-expanded", "true");
    });
});

el.fileInput?.addEventListener("change", () => addFiles(el.fileInput.files));

[el.dropSurface, el.dropzone].filter(Boolean).forEach((target) => {
    target.addEventListener("dragover", (event) => {
        event.preventDefault();
        el.dropzone?.classList.add("is-dragging");
    });
    target.addEventListener("dragleave", () => el.dropzone?.classList.remove("is-dragging"));
    target.addEventListener("drop", (event) => {
        event.preventDefault();
        el.dropzone?.classList.remove("is-dragging");
        addFiles(event.dataTransfer.files);
    });
});

el.dropzone?.addEventListener("keydown", (event) => {
    if (event.key === "Enter" || event.key === " ") {
        event.preventDefault();
        el.fileInput?.click();
    }
});

el.form?.addEventListener("submit", (event) => {
    event.preventDefault();
    startUpload();
});

el.copyButton?.addEventListener("click", () => copyText("Share URL", shareUrl, shareUrl));
el.copyCurlButton?.addEventListener("click", () => copyText("cURL command", getCurlCommand({ full: true })));
el.docPopupClose?.addEventListener("click", closeDoc);
el.modalBackdrop?.addEventListener("click", closeDoc);

el.maxViews?.addEventListener("wheel", (event) => {
    if (el.maxViews.disabled || el.maxViews.readOnly) return;
    event.preventDefault();
    const delta = event.deltaY < 0 ? 1 : -1;
    const modifier = event.ctrlKey && event.shiftKey ? 50 : event.shiftKey ? 15 : event.ctrlKey ? 5 : 1;
    const min = Number.parseInt(el.maxViews.min || "1", 10);
    const max = Number.parseInt(el.maxViews.max || "9999", 10);
    const current = Number.parseInt(el.maxViews.value || String(min), 10);
    el.maxViews.value = String(Math.max(min, Math.min(max, current + (delta * modifier))));
    saveSettings();
    updateTerminal();
});

el.apiKeyInput?.addEventListener("keydown", (event) => {
    const allowed = event.ctrlKey || event.metaKey || event.altKey || [
        "Tab",
        "Shift",
        "Control",
        "Alt",
        "Meta",
        "Escape",
        "ArrowLeft",
        "ArrowRight",
        "ArrowUp",
        "ArrowDown",
        "Home",
        "End",
        "PageUp",
        "PageDown",
    ].includes(event.key);
    if (allowed) return;
    event.preventDefault();
    showToast("Only pasting the API key is supported.", "warning");
    setStatus("Only pasting the API key is supported");
});

el.apiKeyInput?.addEventListener("paste", () => {
    setTimeout(validateApiKeyField, 0);
});

[el.expiry, el.password, el.maxViews, el.boxName, el.customSlug, el.downloadPage, el.allowZip, el.allowPreview, el.keepFilenames, el.privateBox, el.apiKeyMode, el.apiKeyInput].filter(Boolean).forEach((control) => {
    control.addEventListener("input", () => {
        if (control === el.boxName) syncSlugFromName();
        if (control === el.customSlug) {
            const clean = sanitizeSlugInput(el.customSlug.value);
            if (el.customSlug.value !== clean) el.customSlug.value = clean;
            el.customSlug.dataset.auto = "false";
        }
        if (control === el.apiKeyInput) validateApiKeyField();
        saveSettings();
        updateTerminal();
    });
    control.addEventListener("change", () => {
        if (control === el.expiry) syncZipForRetention();
        if (control === el.apiKeyMode) syncApiKeyField();
        saveSettings();
        syncMenuChecks();
        updateTerminal();
    });
});

document.addEventListener("keydown", (event) => {
    if (event.key === "Escape") {
        closeDoc();
        closeMenus();
    }
    if (event.key === "F1") {
        event.preventDefault();
        openDoc("faq");
    }
    if (event.ctrlKey && !event.shiftKey && !event.altKey) {
        const key = event.key.toLowerCase();
        if (key === "o") {
            event.preventDefault();
            el.fileInput?.click();
        }
        if (key === "u") {
            event.preventDefault();
            startUpload();
        }
        if (key === "k") {
            event.preventDefault();
            copyText("cURL command", getCurlCommand({ full: true }));
        }
        if (key === "l") {
            event.preventDefault();
            copyText("Share URL", shareUrl, shareUrl);
        }
    }
});

window.addEventListener("beforeunload", () => {
    files.forEach((item) => {
        if (item.previewURL) URL.revokeObjectURL(item.previewURL);
    });
});

loadSettings();
updateLimitHint();
syncMenuChecks();
renderFiles();
updateTerminal();
