import apx from "./apx.js";
import { getOldestPrivatriids, syncronizeBackend } from "./utils.js";

const bodyEl = document.querySelector("body");
const searchInputEl = document.querySelector("#threadSearchBar");
const threadFilterOptionsElArray = document.querySelector("#threadFilterOptions").querySelectorAll("li");
const threadsContainerEl = document.querySelector("#threadsContainer");
const threadPageEl = document.querySelector("#threadPage");
const messageInputEl = document.getElementById("messageInput");
const messagesContainerEl = document.querySelector("#messagesContainer");
const attachmentsInputEl = document.querySelector("#attachmentsInput");

function formatDate(timestamp) {
    const date = new Date(timestamp);
    const day = String(date.getDate()).padStart(2, "0");
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const year = date.getFullYear();
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");

    return `${day}/${month}/${year} ${hours}:${minutes}`;
};

searchInputEl.addEventListener("input", () => {
    const value = searchInputEl.value.toLowerCase();

    Array.from(threadsContainerEl.children).forEach(child => {
        if (child.querySelector("li > div").textContent.toLowerCase().includes(value)) {
            child.style.display = "block";
        } else {
            child.style.display = "none";
        };
    });
});

threadFilterOptionsElArray.forEach(option => {
    option.addEventListener("click", async () => {
        const filter = option.getAttribute("data-filter");

        if (filter === "date") {
            Array.from(threadsContainerEl.children).sort((a, b) => {
                return a.querySelector("li > span").getAttribute("data-timestamp") - b.querySelector("li > span").getAttribute("data-timestamp");
            }).forEach(child => {
                threadsContainerEl.appendChild(child);
            });
        } else if (filter === "name") {
            Array.from(threadsContainerEl.children).sort((a, b) => {
                return a.querySelector("li > div").textContent.localeCompare(b.querySelector("li > div").textContent);
            }).forEach(child => {
                threadsContainerEl.appendChild(child);
            });
        };
    });
});

const sendNewMessage = async (timestamp, message, attachmentsArray) => {
    let messageTemplate = "";

    await fetch("./message.mustache")
        .then(res => res.text())
        .then(template => {
            messageTemplate = template;
        });

    messageTemplate = Mustache.render(messageTemplate, { timestamp, message, date: formatDate(timestamp) });

    const tempNode = document.createElement("div");
    tempNode.innerHTML = messageTemplate;
    const messageNode = tempNode.firstElementChild;

    attachmentsArray.forEach(attachment => {
        messageNode.querySelector("div.attachmentsContainer").insertAdjacentHTML("beforeend", `<img onclick="attachEventDisplayAttachment(this)" class="w-20 h-20 object-cover rounded" src="data:${attachment.fileType};base64,${attachment.content}" alt="${attachment.filename}"/>`);
    });
    
    return messageNode.outerHTML;
};

const editMessage = async (timestamp, message) => {
    let editMessageTemplate = "";

    await fetch("./editMessage.mustache")
        .then(res => res.text())
        .then(template => {
            editMessageTemplate = template;
        });

    return Mustache.render(editMessageTemplate, { message, date: formatDate(timestamp) });
};

const displayToastAlert = async (message) => {
    let toastAlertTemplate = "";

    await fetch("./toastAlert.mustache")
        .then(res => res.text())
        .then(template => {
            toastAlertTemplate = template;
        });

    return Mustache.render(toastAlertTemplate, { message });
};

sendMessageBtn.addEventListener("click", async () => {
    const message = messageInputEl.value.trim();

    if (message !== "") {
        const messageObj = await (async (publicKey) => {
            const timestamp = Date.now();
            const alias = await apx.crypto.encryptMessage(JSON.parse(localStorage.getItem("apx")).data.headers.xalias, publicKey);
    
            return {
                privatriid: `${threadPageEl.getAttribute("data-uuid")}_${timestamp}`,
                timestamp: timestamp,
                sender_alias: alias,
                message: await apx.crypto.encryptMessage(message, publicKey),
                attachments: await (async () => {
                    const attachmentsArray = [];
                    const localAttachmentsArray = JSON.parse(sessionStorage.getItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`)) || [];

                    if (localAttachmentsArray.length > 0) {
                        for (const attachment of localAttachmentsArray) {
                            attachmentsArray.push(
                                {
                                    fileType: attachment.fileType,
                                    filename: attachment.filename,
                                    content: await apx.crypto.encryptMessage(attachment.content, publicKey)
                                }
                            );
                        };
                    };

                    return attachmentsArray;
                })()
            };
        })((await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messagesContainerEl.firstElementChild.getAttribute("data-timestamp")}`)).publicKey);
        
        const newMessageHTML = await sendNewMessage(messageObj.timestamp, message, JSON.parse(sessionStorage.getItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`)) || []);

        sessionStorage.removeItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`);
        
        messagesContainerEl.insertAdjacentHTML("beforeend", newMessageHTML);
        
        messageInputEl.value = "";

        // Faire un post sur l'endpoint /privatri
        
        await apx.indexedDB.set("privatri", "messages", messageObj);
    };
});

document.addEventListener("DOMContentLoaded", async () => {
    const lastConnection = JSON.parse(localStorage.getItem("lastConnection")) || Date.now();

    await syncronizeBackend(lastConnection);

    const privatriidArray = await getOldestPrivatriids("privatri", "messages");

    const thread = async (name, uuid) => {
        let threadTemplate = "";

        await fetch("./thread.mustache")
            .then(res => res.text())
            .then(template => {
                threadTemplate = template;
            });

        return Mustache.render(threadTemplate, { uuid, name });
    };

    for (const privatriid of privatriidArray) {
        const obj = await apx.indexedDB.get("privatri", "messages", privatriid)
        
        const privateKey = (await apx.indexedDB.get("privatri", "threads", obj.thread)).privateKey;
        const name = (await apx.crypto.decryptMessage(obj.title, privateKey)).data;

        threadsContainerEl.insertAdjacentHTML("beforeend", await thread(name, obj.thread));
    };

    Array.from(threadsContainerEl.children).forEach(child => {
        child.addEventListener("click", async () => {
            const uuid = child.getAttribute("data-uuid");

            const messagesObj = await new Promise((resolve, reject) => {
                const request = indexedDB.open("privatri", 1);

                request.onsuccess = (event) => {
                    const db = event.target.result;

                    const transaction = db.transaction("messages", "readonly");
                    const store = transaction.objectStore("messages");
                    const cursorRequest = store.openCursor();

                    const result = [];
                    cursorRequest.onsuccess = (event) => {
                        const cursor = event.target.result;

                        if (cursor) {
                            const obj = cursor.value;
                            const keyUuid = obj.privatriid.split("_")[0];

                            if (keyUuid === uuid) {
                                result.push(obj);
                            };
                            cursor.continue();
                        } else {
                            resolve(result);
                        };
                    };

                    cursorRequest.onerror = (event) => reject(event);
                };

                request.onerror = (event) => reject(event);
            });

            threadPageEl.setAttribute("data-uuid", uuid);
            threadPageEl.querySelector("#threadName").innerText = (await apx.crypto.decryptMessage(messagesObj[0].title, (await apx.indexedDB.get("privatri", "threads", uuid)).privateKey)).data;

            messagesContainerEl.innerHTML = "";

            let privateKey = "";

            for (const message of messagesObj) {
                if (privateKey === "") {
                    privateKey = (await apx.indexedDB.get("privatri", "threads", message.thread)).privateKey;
                };

                const decryptedMessage = (await apx.crypto.decryptMessage(message.message, privateKey)).data;

                let decryptedAttachments = [];

                if (message.attachments !== undefined) {
                    decryptedAttachments = await (async () => {
                        const attachmentsArray = [];
    
                        if (message.attachments.length > 0) {
                            for (const attachment of message.attachments) {
                                attachmentsArray.push({
                                    fileType: attachment.fileType,
                                    filename: attachment.filename,
                                    content: (await apx.crypto.decryptMessage(attachment.content, privateKey)).data
                                });
                            };
                        };
    
                        return attachmentsArray;
                    })();
                };

                messagesContainerEl.insertAdjacentHTML("beforeend", await sendNewMessage(message.timestamp, decryptedMessage, decryptedAttachments));
            };
        });
    });

    document.querySelectorAll("a").forEach(link => {
        link.addEventListener("click", async (event) => {
            event.preventDefault();

            window.history.replaceState({}, document.title, window.location.pathname);

            const templateName = link.getAttribute("data-template");

            if (templateName === "threadAliasList" || templateName === "threadSettings") {
                window.history.pushState({}, "", `${window.location.pathname}?uuid=${threadPageEl.getAttribute("data-uuid")}`);
            };

            await fetch(`./${templateName}.mustache`)
                .then(res => res.text())
                .then(template => {
                    template = Mustache.render(template, {});

                    bodyEl.innerHTML = template;
                });

            const script = document.createElement("script");

            script.type = "module";
            script.src = `./${templateName}.js`;

            bodyEl.appendChild(script);
        });
    });
});

window.addEventListener("beforeunload", () => {
    if (apx)
    localStorage.setItem("lastConnection", JSON.stringify(Date.now()));
});

window.attachDeleteMessageEvent = async function attachDeleteMessageEvent(btn) {
    const messageEl = btn.parentElement.parentElement.parentElement;

    const privateKey = (await apx.indexedDB.get("privatri", "threads", threadPageEl.getAttribute("data-uuid"))).privateKey;

    const signatureMessage = `${JSON.parse(localStorage.getItem("apx")).data.headers.xalias}_${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`;
    const signature = await apx.crypto.sign(signatureMessage, privateKey);
    
    let verified = false;

    try {
        verified = await fetch("", {
            method: "GET",
            body: JSON.stringify({
                message: signatureMessage,
                signature: signature,
                uuid: threadPageEl.getAttribute("data-uuid"),
                timestamp: messageEl.getAttribute("data-timestamp"),
            })
        });
    } catch (error) {
        console.error("Error while verifying signature:", error);
    };

    const authorAlias = await apx.crypto.decryptMessage((await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`)).sender_alias, privateKey).data;

    if ((JSON.parse(localStorage.getItem("apx")).data.headers.xalias === authorAlias) && (verified === true)) {
        await apx.indexedDB.del("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`);
    
        messageEl.remove();
    } else {
        bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("You don't have the permissions to delete this message."));

        setTimeout(() => {
            bodyEl.lastElementChild.remove();
        }, 3000);
    };
};

window.attachEditMessageEvent = async function attachEditMessageEvent(btn) {
    const messageEl = btn.parentElement.parentElement.parentElement;
   
    const authorAlias = (await apx.crypto.decryptMessage((await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`)).sender_alias, (await apx.indexedDB.get("privatri", "threads", threadPageEl.getAttribute("data-uuid"))).privateKey)).data;
    
    if (JSON.parse(localStorage.getItem("apx")).data.headers.xalias === authorAlias) {
        const messageValue = messageEl.querySelector("div.message").innerText;

        const attachmentsArray = (() => {
            const attachmentsArray = [];

            messageEl.querySelector("div.attachmentsContainer").querySelectorAll("img").forEach(img => {
                attachmentsArray.push(
                    {
                        fileType: img.getAttribute("src").match(/^data:(.*);base64,/)[1],
                        filename: img.getAttribute("alt"),
                        content: img.getAttribute("src").split(",")[1]
                    }
                );
            });

            return attachmentsArray;
        })();

        messageEl.innerHTML = await editMessage(parseInt(messageEl.getAttribute("data-timestamp"), 10), messageEl.querySelector("div.message").innerText);

        messageEl.querySelector("button.cancelEditBtn").addEventListener("click", async () => {
            const messageObj = await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`);

            messageEl.innerHTML = await sendNewMessage(messageObj.timestamp, messageValue, attachmentsArray);
        });

        messageEl.querySelector("button.saveEditBtn").addEventListener("click", async () => {
            const newMessageValue = messageEl.querySelector("textarea").value.trim();

            if (newMessageValue !== "") {
                const privateKey = (await apx.indexedDB.get("privatri", "threads", threadPageEl.getAttribute("data-uuid"))).privateKey;

                const messageObj = await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`);

                messageObj.message = await apx.crypto.encryptMessage(newMessageValue, privateKey),

                await apx.indexedDB.set("privatri", "messages", messageObj);

                messageEl.innerHTML = await sendNewMessage(messageObj.timestamp, newMessageValue, attachmentsArray);
            };
        });
    } else {
        bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("You don't have the permissions to edit this message."));

        setTimeout(() => {
            bodyEl.lastElementChild.remove();
        }, 3000);
    };
};

window.attachEventDisplayAttachment = function(img) {
    let overlay = document.getElementById("imageOverlay");

    if (overlay === null) {
        overlay = document.createElement('div');

        overlay.id = "imageOverlay";
        overlay.style.position = "fixed";
        overlay.style.top = 0;
        overlay.style.left = 0;
        overlay.style.width = "100vw";
        overlay.style.height = "100vh";
        overlay.style.background = "rgba(0, 0, 0, 0.75)";
        overlay.style.display = "flex";
        overlay.style.alignItems = "center";
        overlay.style.justifyContent = "center";
        overlay.style.zIndex = 999;

        overlay.onclick = () => {
            overlay.remove();
        };

        document.body.appendChild(overlay);
    } else {
        overlay.innerHTML = "";
        overlay.style.display = "flex";
    };

    const fullScreenImage = document.createElement("img");

    fullScreenImage.src = img.src;
    fullScreenImage.alt = img.alt;
    fullScreenImage.style.maxWidth = "90vw";
    fullScreenImage.style.maxHeight = "90vh";
    overlay.appendChild(fullScreenImage);
};

document.querySelector("#attachmentsBtn").addEventListener("click", () => {
    attachmentsInputEl.click();
});

attachmentsInputEl.addEventListener("change", async () => {
    const filesArray = Array.from(attachmentsInputEl.files);
    const maxFileSize = 5 * 1024 * 1024;
    const maxSize = 512;

    for (const file of filesArray) {
        if (file.size <= maxFileSize) {
            const attachmentObj = await new Promise((resolve, reject) => {
                const attachmentObj = {
                    fileType: file.type,
                    filename: file.name,
                    content: ""
                };

                const img = new Image();
                
                img.onload = () => {
                    if (img.width > maxSize || img.height > maxSize) {
                        let width = img.width;
                        let height = img.height;

                        if (width > maxSize) {
                            height *= maxSize / width;
                            width = maxSize;
                        };

                        if (height > maxSize) {
                            width *= maxSize / height;
                            height = maxSize;
                        };

                        const canvas = document.createElement("canvas");
                        canvas.width = width;
                        canvas.height = height;

                        const ctx = canvas.getContext("2d");

                        ctx.drawImage(img, 0, 0, width, height);

                        canvas.toBlob(blob => {
                            const reader = new FileReader();

                            reader.onload = (event) => {
                                attachmentObj.content = event.target.result.split(",")[1];

                                resolve(attachmentObj);
                            };

                            reader.onerror = reject;
                            reader.readAsDataURL(blob);
                        }, "image/jpeg", 0.8);
                    } else {
                        const reader = new FileReader();

                        reader.onload = (event) => {
                            attachmentObj.content = event.target.result.split(",")[1];

                            resolve(attachmentObj);
                        };

                        reader.onerror = reject;
                        reader.readAsDataURL(file);
                    };
                };

                img.onerror = reject;
                img.src = URL.createObjectURL(file);

                return attachmentObj;
            });

            if (sessionStorage.getItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`) === null) {
                sessionStorage.setItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`, JSON.stringify([attachmentObj]));
            } else {
                const attachmentsArray = JSON.parse(sessionStorage.getItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`));
                attachmentsArray.push(attachmentObj);

                sessionStorage.setItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`, JSON.stringify(attachmentsArray));
            };
        };
    };
});