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();
}
