import path from "path";
import { fileURLToPath } from "url";
import express, { Request, Response, Application } from "express";
import dotenv from "dotenv";
import helmet from "helmet";
import { mkdirSync } from "fs";
import getFileFromBinaries from "./utils/getFilesFromBinaries";
import getWebAppId from "./utils/getWebAppId";
import emptyDir from "./utils/emptyDir";
import chokidar from "chokidar";
import { handleFileChange, handleFileUnlink } from "./utils/handleFileUpdate";

dotenv.config();

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const destinationDir = path.join(__dirname, "public", "binaries", "webapp-demo");

const app: Application = express();
const port = 8001;

// Location of your web app build files. Edit if these are located somewhere else.
const sourceDir = path.join(__dirname, "..", "dist");

app.use(
    helmet.contentSecurityPolicy({
        directives: {
            defaultSrc: ["'self'"],
            baseUri: ["'self'"],
            scriptSrc: ["'self'"],
            styleSrc: ["'self'"],
            fontSrc: ["'self'"],
            objectSrc: ["'none'"],
            connectSrc: ["'self'"],
            imgSrc: ["'self'", "data:"],
            mediaSrc: ["'self'"],
            formAction: ["'self'"],
            frameAncestors: ["'none'"],
            upgradeInsecureRequests: [],
        },
    }),
);

const init = async () => {
    // Ensure the src and destination directories exist and is empty
    mkdirSync(sourceDir, { recursive: true });
    mkdirSync(destinationDir, { recursive: true });
    emptyDir(destinationDir);

    const watcher = chokidar.watch(sourceDir, {
        depth: 99,
        awaitWriteFinish: {
            stabilityThreshold: 800,
            pollInterval: 100,
        },
    });

    const watcherWebApp = chokidar.watch(destinationDir, {
        depth: 99,
        awaitWriteFinish: {
            stabilityThreshold: 800,
            pollInterval: 100,
        },
    });

    watcher
        .on("add", (filePath, stats) => handleFileChange(destinationDir, filePath, stats))
        .on("change", (filePath, stats) => handleFileChange(destinationDir, filePath, stats))
        .on("unlink", (filePath) => handleFileUnlink(destinationDir, filePath))
        .on("error", (error) => console.log("\x1b[31m%s\x1b[0m", `Watcher error: ${error}`));
    watcherWebApp.on("ready", () => console.log("\x1b[33m%s\x1b[0m", `Watching ${sourceDir} folder for changes`));
};

// Serve static files from the "public" directory
app.use(express.static(path.join(__dirname, "public")));

app.get("/get-webapp-metadata", async (req: Request, res: Response) => {
    const id = await getWebAppId();
    if (!id) {
        res.status(500).json({ message: "No web app id found" });
        return;
    }

    const files = await getFileFromBinaries(destinationDir);

    const indexFilesJs = files.filter((fileObj) => fileObj.file.endsWith(".js") && fileObj.file.startsWith("index-"));
    if (indexFilesJs.length === 0 || indexFilesJs.length > 1) {
        console.log(
            "\x1b[31m%s\x1b[0m",
            `To determin what the main webapp js file is we can only have one index-xxxxx.js, got: ${indexFilesJs.length}`,
        );

        res.status(500).json({ message: `Only one index js file allowed, got: ${indexFilesJs.length}` });
        return;
    }

    const indexFilesCss = files.filter((fileObj) => fileObj.file.endsWith(".css") && fileObj.file.startsWith("index-"));

    if (indexFilesCss.length === 0 || indexFilesCss.length > 1) {
        console.log(
            "\x1b[31m%s\x1b[0m",
            `To determin what the main webapp css file is we can only have one index-xxxxx.css, got: ${indexFilesCss.length}`,
        );

        res.status(500).json({ message: `Only one index css file allowed, got: ${indexFilesCss.length}` });
        return;
    }
    const lastUpdate = files.reduce((latest, fileObj) => {
        return fileObj.mtime > latest ? fileObj.mtime : latest;
    }, new Date(0));

    res.status(200).json({
        id,
        lastUpdate,
        indexFileCss: indexFilesCss[0].file,
        indexFileJs: indexFilesJs[0].file,
    });
    return;
});

app.get("/", (req: Request, res: Response) => {
    res.sendFile(path.join(__dirname, "public", "index.html"));
});

app.listen(port, () => {
    console.log(`Server is running at http://localhost:${port}`);
});

init();
