const express = require('express');
const rateLimit = require("express-rate-limit");
const pdfjs = require("pdfjs-dist/legacy/build/pdf.mjs");
const puppeteer = require("puppeteer");
const fs = require("fs");
const multer = require('multer');
const { randomUUID } = require('crypto');

const FLAG = process.env.FLAG || "flag{******}";

// Rate Limiting
const limiter = rateLimit({
    windowMs: 1 * 60 * 1000,
    limit: 50,
    message: "Too many requests, please try again later."
})

const app = express();
app.use(express.static("public"));
app.set("view engine", "pug")

// File Upload
const upload = multer({
    storage: multer.diskStorage({
        destination: "/app/uploads/",
        filename: (req, file, cb) => { cb(null, randomUUID() + ".pdf") }
    }),
    fileFilter: (req, file, cb) => {
        const isMimeType = file.mimetype === "application/pdf";
        const isPDFExtension = file.originalname.endsWith(".pdf");

        if (isMimeType && isPDFExtension) {
            return cb(null, true);
        }
        else {
            return cb(new Error("Only PDF allowed! No Häx please <3"));
        }
    },
    limits: { fileSize: 1000000 } // Maximum PDF Size: 1 MB
});

app.post("/upload", limiter, async (req, res) => {
    upload.single("invoice")(req, res, async (err) => {
        if (err) {
            res.render("error");
            return;
        }
        let file_id = await req.file.filename.substring(0, req.file.filename.lastIndexOf("."));

        // Make sure there is no malicious content in the PDF.
        if (!(await validate_pdf(file_id))) {
            res.render("error");
            return;
        }

        // Send the PDF to our custömer suppört
        sendToCustömerSuppört(file_id);

        // Render the PDF as an image for the preview
        let pdfThumb = await pdf_to_img(file_id);

        // Provide some fun facts to the custömer about their products
        let did_you_knows = await get_did_you_know(file_id);
        res.render("uploadReceived", { did_you_knows: did_you_knows, pdfThumb: pdfThumb })
        return;

    })
});

app.get("/upload", (req, res) => { res.redirect("/"); });

app.get("/", async (req, res) => {
    res.render("uploadInvoice");
});

async function validate_pdf(id) {
    let path = `/app/uploads/${id}.pdf`;
    const doc = await pdfjs.getDocument({
        url: path,
        standardFontDataUrl: "node_modules/pdfjs-dist/standard_fonts/"
    }).promise;

    if (doc.numPages > 1) {
        console.log("Only single-page PDFs allowed.")
        return false;
    }

    let docPage = await doc.getPage(1);
    let annotations = await docPage.getAnnotations();
    let textContent = await docPage.getTextContent();
    let content = "";

    for (let textItem of textContent["items"]) {
        content += textItem.str;
    }

    for (let annotation of annotations) {
        if (annotation.subtype == "Widget") {
            content += annotation.fieldValue;
        }
        else {
            content += annotation.textContent;
        }
    }

    const unreleased_products = ["Premium FLUX Pencil", "Stridsvagn 103", "FLAG"];
    for (product of unreleased_products) {
        if (content.includes(product)) {
            console.log(`Error: Unreleased product ${product} found.`);
            return false;
        }
    }
    return true;
}

function sendToCustömerSuppört(id) {
    // We fired all our custömer suppört staff and will replace them with AI next month, since everyone loves to talk to custömer suppört bots.
    // Until then, our custömer suppört does nothing :)
    return;
}

async function pdf_to_img(id) {
    // Flatten the PDF so that user content actually appears in the image.
    let path = `/app/uploads/${id}.pdf`;
    const browser = await puppeteer.launch({
        browser: "firefox",
        headless: true,
        ignoreHTTPSErrors: true,
        extraPrefsFirefox: {
            // I heard PDFs can execute JavaScript :o 
            // Please no Häx, so let's disable it.
            'pdfjs.enableScripting': false
        }
    });

    const browser_page = await browser.newPage();
    let url = `file://${path}#page=1,zoom`;
    await Promise.all([
        browser_page.waitForNavigation(),
        browser_page.goto(url)
    ]);

    await browser_page.pdf({ path: path });
    await browser.close();

    // Turn the PDF into a base64-encoded image with PDF.js
    const doc = await pdfjs.getDocument({
        url: path,
        standardFontDataUrl: "node_modules/pdfjs-dist/standard_fonts/"
    }).promise;

    let page = await doc.getPage(1);
    const canvasFactory = doc.canvasFactory;
    const viewport = page.getViewport({ scale: 2.0 });
    const canvasAndContext = canvasFactory.create(
        viewport.width,
        viewport.height
    );

    const renderContext = {
        canvasContext: canvasAndContext.context,
        viewport
    };

    const renderTask = page.render(renderContext);
    await renderTask.promise;
    const image = await canvasAndContext.canvas.toBuffer("image/png");
    return image.toString('base64');
}

async function get_did_you_know(id) {
    let path = `/app/uploads/${id}.pdf`;
    const doc = await pdfjs.getDocument({
        url: path,
        standardFontDataUrl: "node_modules/pdfjs-dist/standard_fonts/"
    }).promise;

    let docPage = await doc.getPage(1);
    let textContent = await docPage.getTextContent();
    let content = "";

    for (item of textContent["items"]) {
        content += item.str;
    }

    // Give the custömer some fun facts about the products they want to return, maybe they will reconsider?
    let products = {
        "KULLABERG": "... that the KULLABERG chair was named after a beautiful nature reserve? ",
        "BLÅHAJ": "... that trans rights are human rights? 🏳️‍⚧️",
        "KNÄCKEBRÖD": "... that KNÄCKEBRÖD is the perfect morning snack with Örtsmör?",
        "REFRÄNG ": "... that REFRÄNG is how germans pronounce their word for chorus?",
        "FLAG": `... that the flag for this challenge is ${FLAG}?`,
    };

    let did_you_know = [
        "... that FLUX has [REDACTED] stores, spread all over the world?",
        "... that the current world record for speedrunning a FLUX store (köttbullar%) is 35:24.557?"
    ];
    for (let product in products) {
        if (content.includes(product)) {
            did_you_know.push(`${products[product]}`);
        }
    }
    return did_you_know;
}

app.listen(80, () => {
    console.log("Server running on port 80");
});
