{% extends "base.html" %}
{% block title %}Upload | ECDA LLM{% endblock %}
{% block body_class %}upload{% endblock %}

{% block content %}
<div class="card card-col upload-card">
    <div class="chat-header">
        <h2>Upload documents</h2>

        <div class="tooltip-container">
            <a class="btn btn-ghost" href="/">Home</a>
            <span class="tooltip">Back to Home</span>
        </div>
    </div>

    <p class="meta">
        Upload PDF, TXT, or DOCX files. You can select multiple files.
    </p>

    <form id="uploadForm" class="upload-form" novalidate>
        <div class="file-upload">
            <label for="fileInput" class="file-upload-label">Choose documents</label>

            <input id="fileInput" type="file" multiple
                accept=".pdf,.txt,.docx,application/pdf,text/plain,application/vnd.openxmlformats-officedocument.wordprocessingml.document" />

            <div id="fileNames" class="file-names">No files selected</div>
        </div>

        <div class="upload-actions">
            <div class="tooltip-container">
                <button id="uploadBtn" class="btn" type="button">Upload</button>
                <span class="tooltip">Upload the files</span>
            </div>

            <div class="tooltip-container">
                <button id="skipBtn" class="btn btn-ghost" type="button">Skip</button>
                <span class="tooltip">Go to chat</span>
            </div>

            <div class="tooltip-container">
                <button id="proceedBtn" class="btn btn-proceed" type="button" disabled>Proceed</button>
                <span class="tooltip">First push Upload</span>
            </div>
        </div>
    </form>

    <div id="uploadedList" class="uploaded-list" style="display:none;"></div>
</div>

<script>
    const fileInput = document.getElementById("fileInput");
    const fileNames = document.getElementById("fileNames");
    const form = document.getElementById("uploadForm");
    const uploadedList = document.getElementById("uploadedList");
    const skipBtn = document.getElementById("skipBtn");
    const proceedBtn = document.getElementById("proceedBtn");
    const uploadBtn = document.getElementById("uploadBtn");

    const baseUrl = sessionStorage.getItem("LLM_BASE_URL");
    const apiKey = sessionStorage.getItem("LLM_API_KEY") || "lm-studio";
    const model = sessionStorage.getItem("LLM_MODEL");
    const embedModel = sessionStorage.getItem("EMBED_MODEL") || "nomic-embed-text-v1.5";

    if (!baseUrl || !model) window.location.href = "/";

    form.addEventListener("submit", (e) => e.preventDefault());

    let pendingFiles = [];
    let serverFiles = [];

    function setProceedEnabled(enabled) {
        proceedBtn.disabled = !enabled;
    }

    function escapeHtml(str) {
        return String(str || "")
            .replaceAll("&", "&amp;")
            .replaceAll("<", "&lt;")
            .replaceAll(">", "&gt;")
            .replaceAll('"', "&quot;")
            .replaceAll("'", "&#039;");
    }

    function renderFileNames(expanded) {
        if (!pendingFiles.length) {
            fileNames.textContent = "No files selected";
            return;
        }

        const names = pendingFiles.map(f => f.name);
        const visible = expanded ? names : names.slice(0, 3);

        fileNames.innerHTML = "";

        const textSpan = document.createElement("span");
        textSpan.textContent = visible.join(" • ");
        fileNames.appendChild(textSpan);

        if (names.length > 3) {
            const space = document.createElement("span");
            space.textContent = " ";
            fileNames.appendChild(space);

            const toggle = document.createElement("button");
            toggle.type = "button";
            toggle.className = "btn btn-ghost btn-mini";
            toggle.textContent = expanded ? "Show less..." : "Show more...";

            toggle.addEventListener("click", () => {
                const next = !expanded;
                sessionStorage.setItem("UPLOAD_FILENAMES_EXPANDED", next ? "1" : "0");
                renderFileNames(next);
            });

            fileNames.appendChild(toggle);
        }
    }

    function refreshFileNames() {
        const expanded = sessionStorage.getItem("UPLOAD_FILENAMES_EXPANDED") === "1";
        renderFileNames(expanded);
    }

    function syncInputFilesFromPending() {
        const dt = new DataTransfer();
        pendingFiles.forEach(f => dt.items.add(f));
        fileInput.files = dt.files;
    }

    function renderAll() {
        uploadedList.innerHTML = "";

        const hasServer = serverFiles.length > 0;
        const hasPending = pendingFiles.length > 0;

        if (!hasServer && !hasPending) {
            uploadedList.style.display = "none";
            setProceedEnabled(false);
            return;
        }

        uploadedList.style.display = "block";

        if (hasServer) {
            const title1 = document.createElement("div");
            title1.className = "meta";
            title1.textContent = "Uploaded files:";
            uploadedList.appendChild(title1);

            const ul1 = document.createElement("ul");
            ul1.className = "uploaded-ul";

            serverFiles.forEach((f) => {
                const li = document.createElement("li");
                li.className = "uploaded-li";

                const safeName = escapeHtml(f.original || "");
                const sizeKb = Math.round((f.size || 0) / 1024);

                li.innerHTML = `<span class="uploaded-name">${safeName} (${sizeKb} KB)</span>`;
                ul1.appendChild(li);
            });

            uploadedList.appendChild(ul1);
        }

        if (hasPending) {
            const title2 = document.createElement("div");
            title2.className = "meta";
            title2.style.marginTop = "12px";
            title2.textContent = "Selected files (not uploaded yet):";
            uploadedList.appendChild(title2);

            const ul2 = document.createElement("ul");
            ul2.className = "uploaded-ul";

            pendingFiles.forEach((f, idx) => {
                const li = document.createElement("li");
                li.className = "uploaded-li";

                const safeName = escapeHtml(f.name);
                const sizeKb = Math.round((f.size || 0) / 1024);

                li.innerHTML = `
                    <span class="uploaded-name">${safeName} (${sizeKb} KB)</span>
                    <span class="delete-filename" role="button" tabindex="0" title="Remove">
                      <i class="fas fa-trash-alt"></i>
                    </span>
                `;

                const del = li.querySelector(".delete-filename");
                del.addEventListener("click", () => {
                    pendingFiles.splice(idx, 1);
                    syncInputFilesFromPending();
                    refreshFileNames();
                    renderAll();
                    setProceedEnabled(pendingFiles.length > 0);
                });

                del.addEventListener("keydown", (e) => {
                    if (e.key === "Enter" || e.key === " ") {
                        e.preventDefault();
                        del.click();
                    }
                });

                ul2.appendChild(li);
            });

            uploadedList.appendChild(ul2);
        }

        setProceedEnabled(hasPending);
    }

    async function loadExistingUploads() {
        try {
            const res = await fetch("/api/uploads");
            const data = await res.json();
            if (!res.ok || !data.ok) return;
            serverFiles = data.files || [];
            renderAll();
        } catch (e) {
            console.warn("Could not load existing uploads", e);
        }
    }

    fileInput.addEventListener("change", () => {
        const newlySelected = fileInput.files ? Array.from(fileInput.files) : [];
        if (newlySelected.length === 0) return;

        const existingKeys = new Set(
            pendingFiles.map(f => `${f.name}__${f.size}__${f.lastModified}`)
        );

        for (const f of newlySelected) {
            const key = `${f.name}__${f.size}__${f.lastModified}`;
            if (!existingKeys.has(key)) {
                pendingFiles.push(f);
                existingKeys.add(key);
            }
        }

        syncInputFilesFromPending();
        refreshFileNames();
        setProceedEnabled(false);
        fileInput.value = "";
    });

    uploadBtn.addEventListener("click", () => {
        if (!pendingFiles.length) {
            Swal.fire({ icon: "info", title: "No files selected", text: "Choose files first." });
            return;
        }
        renderAll();
        setProceedEnabled(true);
    });

    skipBtn.addEventListener("click", () => (window.location.href = "/chat"));

    async function doUploadAndEmbed() {
        if (!pendingFiles.length) {
            Swal.fire({ icon: "info", title: "No files selected", text: "Choose files first." });
            return;
        }

        syncInputFilesFromPending();

        const filesToUpload = [...pendingFiles];
        const fd = new FormData();
        for (const f of filesToUpload) fd.append("files", f);

        const fileStatuses = {};
        filesToUpload.forEach((f) => {
            fileStatuses[f.name] = "pending";
        });

        function renderModalFileList(container) {
            container.innerHTML = "";

            filesToUpload.forEach((f) => {
                const status = fileStatuses[f.name] || "pending";

                let icon = "•";
                let color = "#6b7280";
                let text = "Pending";

                if (status === "saving") {
                    icon = "⟳";
                    color = "#2563eb";
                    text = "Saving...";
                } else if (status === "indexing") {
                    icon = "⟳";
                    color = "#2563eb";
                    text = "Embedding...";
                } else if (status === "done") {
                    icon = "✓";
                    color = "#16a34a";
                    text = "Done";
                } else if (status === "error") {
                    icon = "✕";
                    color = "#dc2626";
                    text = "Failed";
                }

                const row = document.createElement("div");
                row.style.display = "flex";
                row.style.justifyContent = "space-between";
                row.style.alignItems = "center";
                row.style.gap = "8px";
                row.style.fontSize = "13px";
                row.style.marginBottom = "6px";

                row.innerHTML = `
                    <span style="color:${color};font-weight:700;min-width:16px">${icon}</span>
                    <span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(f.name)}</span>
                    <span style="color:${color};font-size:12px;white-space:nowrap">${text}</span>
                `;

                container.appendChild(row);
            });
        }

        await Swal.fire({
            title: "Uploading & embedding…",
            html: `
                <div style="text-align:left">
                    <div id="uploadStage" style="margin-bottom:10px;">Starting...</div>

                    <div style="width:100%;height:16px;background:#e5e7eb;border-radius:999px;overflow:hidden;">
                        <div id="uploadBar" style="width:0%;height:100%;background:#2563eb;transition:width .2s ease;"></div>
                    </div>

                    <div id="uploadPercent" style="margin-top:8px;font-weight:600;">0%</div>

                    <div style="margin-top:12px;font-weight:600;">Processing files:</div>
                    <div id="fileList" style="margin-top:8px;max-height:220px;overflow:auto;"></div>
                </div>
            `,
            allowOutsideClick: false,
            allowEscapeKey: false,
            showConfirmButton: false,
            didOpen: async () => {
                Swal.showLoading();

                const popup = Swal.getPopup();
                const bar = popup.querySelector("#uploadBar");
                const percent = popup.querySelector("#uploadPercent");
                const stage = popup.querySelector("#uploadStage");
                const fileList = popup.querySelector("#fileList");

                renderModalFileList(fileList);

                try {
                    const res = await fetch("/api/upload_stream", {
                        method: "POST",
                        headers: {
                            "X-LLM-Base-URL": baseUrl,
                            "X-LLM-API-Key": apiKey,
                            "X-LLM-Model": model,
                            "X-Embed-Model": embedModel,
                        },
                        body: fd,
                    });

                    if (!res.ok || !res.body) {
                        let msg = "Upload stream failed.";
                        try { msg = await res.text(); } catch {}
                        throw new Error(msg);
                    }

                    const reader = res.body.getReader();
                    const decoder = new TextDecoder("utf-8");
                    let buffer = "";
                    let finalData = null;

                    while (true) {
                        const { value, done } = await reader.read();
                        if (done) break;

                        buffer += decoder.decode(value, { stream: true });
                        const events = buffer.split("\n\n");
                        buffer = events.pop() || "";

                        for (const evt of events) {
                            const line = evt.split("\n").find((l) => l.startsWith("data: "));
                            if (!line) continue;

                            const payload = JSON.parse(line.slice(6));

                            if (typeof payload.progress === "number") {
                                bar.style.width = `${payload.progress}%`;
                                percent.textContent = `${payload.progress}%`;
                            }

                            if (payload.stage) {
                                const labelMap = {
                                    starting: "Starting...",
                                    saving: "Saving file...",
                                    saved: "File saved.",
                                    indexing: "Embedding and indexing...",
                                    indexed: "File indexed.",
                                    error: "Error during processing.",
                                    done: "Completed.",
                                };
                                stage.textContent = labelMap[payload.stage] || payload.stage;
                            }

                            if (payload.file && payload.stage === "saving") {
                                fileStatuses[payload.file] = "saving";
                            }

                            if (payload.file && (payload.stage === "saved" || payload.stage === "indexing")) {
                                fileStatuses[payload.file] = "indexing";
                            }

                            if (payload.file && payload.stage === "indexed") {
                                fileStatuses[payload.file] = "done";
                            }

                            if (payload.file && payload.stage === "error") {
                                fileStatuses[payload.file] = "error";
                            }

                            renderModalFileList(fileList);

                            if (payload.done) {
                                finalData = payload;
                            }
                        }
                    }

                    if (!finalData) {
                        throw new Error("Upload completed, but no final result was returned.");
                    }

                    if (!finalData.ok && !(finalData.rag?.files_indexed > 0)) {
                        Swal.close();
                        await Swal.fire({
                            icon: "error",
                            title: "Upload failed",
                            text: finalData.detail || finalData.message || "Unknown error",
                        });
                        return;
                    }

                    if (!finalData.rag) {
                        Swal.close();
                        await Swal.fire({
                            icon: "error",
                            title: "Indexing failed",
                            text: "Upload succeeded, but backend returned no rag info.",
                        });
                        return;
                    }

                    const chunksAdded = Number(finalData.rag?.chunks_added || 0);
                    const filesIndexed = Number(finalData.rag?.files_indexed || 0);
                    const ragErrors = Array.isArray(finalData.rag?.errors) ? finalData.rag.errors : [];

                    if (chunksAdded <= 0 || filesIndexed <= 0) {
                        Swal.close();
                        await Swal.fire({
                            icon: "error",
                            title: "Indexing failed",
                            html: `
                                <div style="text-align:left">
                                    <div><b>Files were uploaded, but indexing did not add usable vector chunks.</b></div>
                                    <pre style="white-space:pre-wrap;font-size:12px;">${escapeHtml(
                                        JSON.stringify(finalData.rag, null, 2)
                                    )}</pre>
                                </div>
                            `,
                        });
                        return;
                    }

                    filesToUpload.forEach((f) => {
                        if (fileStatuses[f.name] !== "error") {
                            fileStatuses[f.name] = "done";
                        }
                    });
                    renderModalFileList(fileList);

                    bar.style.width = "100%";
                    percent.textContent = "100%";
                    stage.textContent = "Completed.";

                    await loadExistingUploads();

                    await new Promise((resolve) => setTimeout(resolve, 500));

                    Swal.close();

                    pendingFiles = [];
                    fileInput.value = "";
                    sessionStorage.setItem("UPLOAD_FILENAMES_EXPANDED", "0");
                    refreshFileNames();
                    renderAll();

                    if (ragErrors.length > 0) {
                        await Swal.fire({
                            icon: "warning",
                            title: "Uploaded with warnings",
                            html: `
                                <div style="text-align:left">
                                    <div><b>Some files were indexed, but there were warnings:</b></div>
                                    <pre style="white-space:pre-wrap;font-size:12px;">${escapeHtml(
                                        JSON.stringify(finalData.rag, null, 2)
                                    )}</pre>
                                </div>
                            `,
                        });
                    }

                    window.location.href = "/chat";

                } catch (err) {
                    Swal.close();

                    const msg = String(err || "");
                    const isTimeout =
                        /timeout|timed out|ETIMEDOUT|ECONNABORTED/i.test(msg);

                    await Swal.fire({
                        icon: "error",
                        title: "Network error",
                        html: `
                        <div style="text-align:left; line-height:1.45">
                            <div style="margin-bottom:8px;">
                                <b>What happened:</b><br>
                                <span style="font-size:12px; opacity:.85">${escapeHtml(msg)}</span>
                            </div>

                            <div style="margin-top:10px;">
                                <b>Try this:</b>
                                <ul style="margin:6px 0 0 18px;">
                                    <li>Upload fewer files at once (1–2 first), then proceed in batches.</li>
                                    <li>If using a local LLM server, confirm it’s running and reachable at the configured Base URL.</li>
                                    <li>${isTimeout ? "Increase server timeouts (upload/embed can be slow for big PDFs)." : "Retry once (temporary network hiccups happen)."}</li>
                                </ul>
                            </div>
                        </div>`,
                    });
                }
            },
        });
    }

    proceedBtn.addEventListener("click", doUploadAndEmbed);

    loadExistingUploads();
</script>

{% endblock %}