{% extends "base.html" %}
{% block title %}Chat | ECDA LLM{% endblock %}

{% block content %}
<div class="card card-col chat-container">
    <div class="chat-header">
        <h2>Chat about Documents</h2>
        <div>
            <div class="tooltip-container">
                <a class="btn btn-ghost" href="/upload">Upload</a>
                <span class="tooltip">Upload more?</span>
            </div>
            <div class="tooltip-container">
                <a class="btn btn-ghost" href="/">Home</a>
                <span class="tooltip">Back home</span>
            </div>

            <div class="tooltip-container">
                <button id="resetBtn" class="btn btn-ghost" type="button">Delete</button>
                <span class="tooltip">Delete all documents</span>
            </div>
            <div class="tooltip-container">
                <button id="saveBtn" class="btn btn-ghost" type="button">Save</button>
                <span class="tooltip">Save all chat as PDF</span>
            </div>
        </div>
    </div>

    <div class="meta" id="meta"></div>
    <div id="filesMeta" class="files-meta"></div>

    <div id="messages" class="messages" aria-live="polite"></div>

    <form id="chatForm" class="chat-form">
        <input id="messageInput" class="input" type="text" placeholder="Type a message…" autocomplete="off" />
        <button class="btn" type="submit">Send</button>
    </form>
</div>

<script>
    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 = "/";

    // NEW: stable session id for "continue"
    // Use sessionStorage so a refresh keeps it; a new browser tab gets a new one.
    let chatSessionId = sessionStorage.getItem("CHAT_SESSION_ID");
    if (!chatSessionId) {
        if (crypto && crypto.randomUUID) chatSessionId = crypto.randomUUID();
        else chatSessionId = String(Date.now()) + "_" + Math.random().toString(16).slice(2);
        sessionStorage.setItem("CHAT_SESSION_ID", chatSessionId);
    }

    const metaEl = document.getElementById("meta");
    if (metaEl) {
        metaEl.innerHTML = `Chat: ${model} @ ${baseUrl} <br> Embeddings: ${embedModel} (local)`;
    }

    const messagesEl = document.getElementById("messages");
    const form = document.getElementById("chatForm");
    const input = document.getElementById("messageInput");
    const sendBtn = form.querySelector("button[type='submit']");
    const resetBtn = document.getElementById("resetBtn");
    const saveBtn = document.getElementById("saveBtn");


    function forceLinksNewTab(containerEl) {
        if (!containerEl) return;
        containerEl.querySelectorAll("a[href]").forEach(a => {
            a.setAttribute("target", "_blank");
            a.setAttribute("rel", "noopener noreferrer");
        });
    }


    function renderMarkdownSafe(mdText) {
        let html = marked.parse(mdText || "", { breaks: true });

        // If it's a single <p>...</p>, unwrap it to avoid block margins
        html = html.trim();
        if (html.startsWith("<p>") && html.endsWith("</p>")) {
            html = html.slice(3, -4);
        }

        return DOMPurify.sanitize(html);
    }


    function scrollToBottom() {
        messagesEl.scrollTop = messagesEl.scrollHeight;
    }

    const chatLog = [];

    // KEEP ONLY ONE appendMessage (this one logs to chatLog)
    function appendMessage(role, text) {
        const row = document.createElement("div");
        row.className = "msg " + (role === "user" ? "msg-user" : "msg-bot");

        if (role === "assistant") {
            row.innerHTML = renderMarkdownSafe(text);
            forceLinksNewTab(row);
        } else {
            row.textContent = text;
        }


        messagesEl.appendChild(row);
        scrollToBottom();

        // Log plain text for PDF
        chatLog.push({
            role: role === "user" ? "user" : "assistant",
            text: row.textContent || ""
        });

        return row;
    }


    function updateMessage(el, newText) {
        if (!el) return;

        // assistant bubbles are markdown-rendered
        if (el.classList.contains("msg-bot")) {
            el.innerHTML = renderMarkdownSafe(newText);
            forceLinksNewTab(el);
        } else {
            el.textContent = newText;
        }


        scrollToBottom();

        // Update last assistant message in log
        for (let i = chatLog.length - 1; i >= 0; i--) {
            if (chatLog[i].role === "assistant") {
                chatLog[i].text = el.textContent || "";
                break;
            }
        }
    }


    function setBusy(isBusy) {
        input.disabled = isBusy;
        if (sendBtn) sendBtn.disabled = isBusy;
        if (resetBtn) resetBtn.disabled = isBusy;
    }

    async function loadUploadsIntoMeta() {
        const filesMeta = document.getElementById("filesMeta");
        if (!filesMeta) return;

        try {
            const res = await fetch("/api/uploads");
            const data = await res.json();

            if (!data.ok) {
                filesMeta.textContent = "Documents: (could not load)";
                return;
            }

            const files = data.files || [];
            if (!files.length) {
                filesMeta.textContent = "Documents (0): none";
                return;
            }

            filesMeta.textContent = `Documents (${files.length}): ${files.map(f => f.original).join(" • ")}`;
        } catch (e) {
            filesMeta.textContent = "Documents: (could not load)";
        }
    }

    loadUploadsIntoMeta();

    // ---- Reset handler ----
    resetBtn.addEventListener("click", async () => {
        const confirm = await Swal.fire({
            icon: "warning",
            title: "Delete everything?",
            text: "All uploaded documents and the vector database will be deleted.",
            showCancelButton: true,
            confirmButtonText: "Yes, delete all",
            cancelButtonText: "Cancel",
        });

        if (!confirm.isConfirmed) return;

        setBusy(true);

        Swal.fire({
            title: "Deleting…",
            allowOutsideClick: false,
            allowEscapeKey: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await fetch("/api/reset", { method: "POST" });
            const data = await res.json();

            if (!res.ok || !data.ok) {
                throw new Error(data.detail || data.message || "Delete failed");
            }

            await Swal.fire({
                icon: "success",
                title: "Deleted",
                text: "All uploads and vector DB data were removed. Returning to home…",
                timer: 900,
                showConfirmButton: false
            });

            sessionStorage.clear();
            window.location.href = "/";

        } catch (err) {
            await Swal.fire({
                icon: "error",
                title: "Delete failed",
                text: String(err)
            });
        } finally {
            setBusy(false);
        }
    });


    function startTypingEffect(targetEl) {
        let pending = "";
        let timer = null;
        let full = "";

        function render() {
            targetEl.innerHTML = renderMarkdownSafe(full);
            forceLinksNewTab(targetEl);
            scrollToBottom();
        }

        function flushChunk() {
            if (!pending) return;
            // flush a small chunk to feel “typed”
            const n = Math.min(pending.length, 12);
            full += pending.slice(0, n);
            pending = pending.slice(n);
            render();
        }

        function start() {
            if (timer) return;
            timer = setInterval(() => {
                flushChunk();
                if (!pending) {
                    clearInterval(timer);
                    timer = null;
                }
            }, 18);
        }

        return {
            push(delta) {
                pending += delta;
                start();
            },
            stop() {
                if (timer) clearInterval(timer);
                timer = null;
                if (pending) {
                    full += pending;
                    pending = "";
                }
                render();
            },
            // allow caller to set initial text if needed
            setText(text) {
                full = text || "";
                pending = "";
                render();
            },
            getText() {
                return full;
            }
        };
    }

    // ---- Chat submit (STREAMING) ----
    form.addEventListener("submit", async (e) => {
        e.preventDefault();

        const text = input.value.trim();
        if (!text) return;

        appendMessage("user", text);

        input.value = "";
        input.focus();
        setBusy(true);

        const botMsg = appendMessage("assistant", "Thinking...");
        let thinkingStep = 0;
        const thinkingTimer = setInterval(() => {
            thinkingStep = (thinkingStep + 1) % 4;
            botMsg.textContent = "Thinking" + ".".repeat(thinkingStep);
        }, 300);

        botMsg.classList.add("thinking");

        const typer = startTypingEffect(botMsg);

        try {
            const res = await fetch("/api/chat_stream", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "X-Session-Id": chatSessionId,          // NEW: required for continue
                    "X-LLM-Base-URL": baseUrl,
                    "X-LLM-API-Key": apiKey,
                    "X-LLM-Model": model,
                    "X-Embed-Model": embedModel
                },
                body: JSON.stringify({
                    message: text,
                    system: "You are a helpful assistant.",
                    temperature: 0.7,
                    top_k: 3
                })
            });

            if (!res.ok || !res.body) {
                clearInterval(thinkingTimer);
                botMsg.classList.remove("thinking");
                typer.stop();
                updateMessage(botMsg, `Server error (${res.status})`);
                return;
            }

            botMsg.classList.remove("thinking");
            let clearedThinking = false;

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

            let buffer = "";
            let fullText = "";

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

                buffer += decoder.decode(value, { stream: true });

                const parts = buffer.split("\n\n");
                buffer = parts.pop() || "";

                for (const part of parts) {
                    if (!part.startsWith("data: ")) continue;

                    const jsonStr = part.slice(6).trim();
                    if (!jsonStr) continue;

                    let payload;
                    try { payload = JSON.parse(jsonStr); }
                    catch { continue; }

                    if (payload.delta) {
                        if (!clearedThinking) {
                            clearInterval(thinkingTimer);
                            typer.setText("");
                            clearedThinking = true;
                        }
                        fullText += payload.delta;
                        typer.push(payload.delta);
                    }

                    if (payload.done) {
                        clearInterval(thinkingTimer);
                        typer.stop();
                        botMsg.innerHTML = renderMarkdownSafe(fullText || "(empty response)");
                        forceLinksNewTab(botMsg);
                        scrollToBottom();

                        // update chatLog plain text
                        for (let i = chatLog.length - 1; i >= 0; i--) {
                            if (chatLog[i].role === "assistant") {
                                chatLog[i].text = botMsg.textContent || "";
                                break;
                            }
                        }
                        break;
                    }
                }
            }

            typer.stop();
            updateMessage(botMsg, fullText || "(empty response)");

        } catch (err) {
            typer.stop();
            botMsg.classList.remove("thinking");
            updateMessage(botMsg, "Network error: " + String(err));
        } finally {
            setBusy(false);
            input.focus();
        }
    });

    // ---- Save PDF ----
    saveBtn.addEventListener("click", async () => {
        if (!chatLog.length) {
            Swal.fire({ icon: "info", title: "Nothing to save", text: "Chat is empty." });
            return;
        }

        // ✅ Privacy warning confirmation
        const confirm = await Swal.fire({
            icon: "warning",
            title: "Before you save",
            html: "This conversation may include <b>personal information</b>.<br>Please be careful where you store/share the PDF.",
            showCancelButton: true,
            confirmButtonText: "Save PDF",
            cancelButtonText: "Cancel",
            reverseButtons: true,
        });

        if (!confirm.isConfirmed) return;

        try {
            setBusy(true);

            const filesMetaEl = document.getElementById("filesMeta");
            const metaText =
                `Chat: ${model} @ ${baseUrl}\n` +
                `Embeddings: ${embedModel} (local)\n` +
                (filesMetaEl ? filesMetaEl.textContent : "");

            const res = await fetch("/api/export_chat_pdf", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({
                    title: "ECDA LLM Chat Export",
                    meta: metaText,
                    messages: chatLog,
                }),
            });

            if (!res.ok) {
                const t = await res.text();
                throw new Error(t || `Export failed (${res.status})`);
            }

            const blob = await res.blob();
            const url = URL.createObjectURL(blob);

            const a = document.createElement("a");
            a.href = url;

            const cd = res.headers.get("Content-Disposition") || "";
            const match = cd.match(/filename="([^"]+)"/i);
            a.download = match ? match[1] : "ecda_chat_export.pdf";

            document.body.appendChild(a);
            a.click();
            a.remove();

            URL.revokeObjectURL(url);

            await Swal.fire({ icon: "success", title: "Saved", timer: 800, showConfirmButton: false });
        } catch (err) {
            await Swal.fire({ icon: "error", title: "Save failed", text: String(err) });
        } finally {
            setBusy(false);
        }
    });


</script>
{% endblock %}