"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const console_1 = require("../../utils/console");
const express = require("express");
const fs = require("fs");
const bodyParser = require("body-parser");
const socket_1 = require("../socket");
const fileQueue_1 = require("../../utils/fileQueue");
// import gitpod from '../gitpod'
const exercise_1 = require("../config/exercise");
const session_1 = require("../../managers/session");
const telemetry_1 = require("../telemetry");
const creatorUtilities_1 = require("../../utils/creatorUtilities");
const rigoActions_1 = require("../../utils/rigoActions");
const sidebarGenerator_1 = require("../../utils/sidebarGenerator");
const path = require("path");
// import { eventManager } from "../../utils/osOperations"
const withHandler = (func) => (req, res) => {
    try {
        func(req, res);
    }
    catch (error) {
        console_1.default.debug(error);
        const _err = {
            message: error.message || "There has been an error",
            status: error.status || 500,
            type: error.type || null,
        };
        console_1.default.error(_err.message);
        // send rep to the server
        res.status(_err.status);
        res.json(_err);
    }
};
async function default_1(app, configObject, configManager) {
    const { config, exercises } = configObject;
    const session = await session_1.default.get(configManager === null || configManager === void 0 ? void 0 : configManager.get());
    const dispatcher = fileQueue_1.default.dispatcher({
        create: true,
        path: `${config === null || config === void 0 ? void 0 : config.dirPath}/vscode_queue.json`,
    });
    app.get("/config", withHandler((_, res) => {
        const confObject = configManager.get();
        res.json(confObject);
    }));
    // Added this line to parse the json body
    const jsonBodyParser = bodyParser.json({ limit: "10mb" });
    // Trying to log in from frontend
    app.post("/login", jsonBodyParser, withHandler(async (req, res) => {
        const email = req.body.email;
        const password = req.body.password;
        session_1.default.destroy();
        const payload = await session_1.default.loginWeb(email, password);
        res.json(payload);
    }));
    app.post("/logout", jsonBodyParser, withHandler(async (req, res) => {
        session_1.default.destroy();
        res.json({ message: "You've logged out from Breathecode and Rigobot" });
    }));
    app.post("/set-rigobot-token", jsonBodyParser, withHandler(async (req, res) => {
        const token = req.body.token;
        // Ensure token is provided in the request body
        if (!token) {
            return res.status(400).json({ error: "Token is required" });
        }
        try {
            const tokenSaved = await session_1.default.setRigoToken(token);
            // Check if the token was saved successfully
            if (tokenSaved) {
                res.json({ status: "ok" });
            }
            else {
                res.status(500).json({ error: "Failed to save the token" });
            }
        }
        catch (_a) {
            // Handle any unexpected errors during the process
            res.status(500).json({ error: "Internal server error" });
        }
    }));
    app.post("/set-tab-hash", jsonBodyParser, withHandler(async (req, res) => {
        const hash = req.body.hash;
        if (!hash) {
            return res.status(400).json({ error: "Token is required" });
        }
        try {
            const hashSaved = await session_1.default.setTabHash(hash);
            if (hashSaved) {
                res.json({ status: "ok" });
            }
            else {
                res.status(500).json({ error: "Failed to save the token" });
            }
        }
        catch (_a) {
            res.status(500).json({ error: "Internal server error" });
        }
    }));
    app.post("/set-session-key", jsonBodyParser, withHandler(async (req, res) => {
        const sessionKey = req.body.sessionKey;
        if (!sessionKey) {
            return res.status(400).json({ error: "Token is required" });
        }
        try {
            const sessionSaved = await session_1.default.setSessionKey(sessionKey);
            if (sessionSaved) {
                res.json({ status: "ok" });
            }
            else {
                res.status(500).json({ error: "Failed to save the token" });
            }
        }
        catch (_a) {
            res.status(500).json({ error: "Internal server error" });
        }
    }));
    app.post("/set-session", jsonBodyParser, withHandler(async (req, res) => {
        const payload = req.body.payload;
        if (!payload) {
            return res.status(400).json({ error: "Token is required" });
        }
        try {
            const payloadSaved = await session_1.default.setPayload(payload);
            if (payloadSaved) {
                res.json({ status: "ok" });
                socket_1.default.sessionRefreshed(payload);
            }
            else {
                res.status(500).json({ error: "Failed to save the token" });
            }
        }
        catch (_a) {
            res.status(500).json({ error: "Internal server error" });
        }
    }));
    app.get("/telemetry", async (req, res) => {
        const telemetry = await telemetry_1.default.retrieve();
        res.json(telemetry);
    });
    app.post("/telemetry", jsonBodyParser, async (req, res) => {
        const telemetry = req.body;
        await telemetry_1.default.save(telemetry);
        res.json({ message: "Telemetry saved successfully" });
    });
    app.get("/check/rigo/status", withHandler(async (_, res) => {
        const payload = await session_1.default.getPayload();
        if (payload && payload.rigobot && payload.rigobot.key) {
            res.json({ rigoToken: payload.rigobot.key, payload: payload });
        }
        else {
            res
                .status(400)
                .json({ details: `Rigobot token not found, please log in first!` });
        }
    }));
    // symbolic link to maintain path compatiblity
    const fetchStaticAsset = withHandler((req, res) => {
        const filePath = `${config === null || config === void 0 ? void 0 : config.dirPath}/assets/${req.params.filePath}`;
        if (!fs.existsSync(filePath))
            throw new Error("File not found: " + filePath);
        const content = fs.readFileSync(filePath);
        res.write(content);
        res.end();
    });
    app.get(`${(config === null || config === void 0 ? void 0 : config.dirPath.indexOf("./")) === 0 ?
        config.dirPath.slice(1) :
        config === null || config === void 0 ? void 0 : config.dirPath}/assets/:filePath`, fetchStaticAsset);
    app.get("/assets/:filePath", fetchStaticAsset);
    app.get("/exercise", withHandler((_, res) => {
        res.json(exercises);
    }));
    app.get("/exercise/:slug/readme", withHandler(({ params: { slug }, query: { lang } }, res) => {
        const exercise = configManager.getExercise(slug);
        if (exercise && exercise.getReadme) {
            const readme = exercise.getReadme(lang || null);
            res.json(readme);
        }
        else {
            res.status(400);
        }
    }));
    app.get("/exercise/:slug/report", withHandler(({ params: { slug } }, res) => {
        const exercise = configManager.getExercise(slug);
        if (exercise && exercise.getTestReport) {
            const report = exercise.getTestReport();
            res.json(JSON.stringify(report));
        }
    }));
    app.get("/exercise/:slug", withHandler((req, res) => {
        var _a, _b, _c, _d, _e;
        // no need to re-start exercise if it's already started
        if (configObject.currentExercise &&
            req.params.slug === configObject.currentExercise) {
            const exercise = configManager.getExercise(req.params.slug);
            res.json(exercise);
            if (exercise.position) {
                telemetry_1.default.registerStepEvent(exercise.position, "open_step", {});
            }
            return;
        }
        const exercise = configManager.startExercise(req.params.slug);
        if (exercise.position) {
            telemetry_1.default.registerStepEvent(exercise.position, "open_step", {});
        }
        if (((_a = configObject.config) === null || _a === void 0 ? void 0 : _a.editor.agent) !== "os") {
            dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug);
        }
        const entries = new Set(Object.keys(config === null || config === void 0 ? void 0 : config.entries).map(lang => config === null || config === void 0 ? void 0 : config.entries[lang]));
        // if we are in incremental grading, the entry file can by dinamically detected
        // based on the changes the student is making during the exercise
        if ((config === null || config === void 0 ? void 0 : config.grading) === "incremental") {
            const scanedFiles = fs.readdirSync("./");
            // update the file hierarchy with updates
            exercise.files = [
                ...exercise.files.filter(f => f.name.includes("test.")),
                ...(0, exercise_1.filterFiles)(scanedFiles),
            ];
            console_1.default.debug(`Exercise updated files: `, exercise.files);
        }
        const detected = (0, exercise_1.detect)(configObject, exercise.files
            .filter(fileName => entries.has(fileName.name))
            .map(f => f.name || f));
        // if a new language for the testing engine is detected, we replace it
        // if not we leave it as it was before
        if ((config === null || config === void 0 ? void 0 : config.language) && !["", "auto"].includes(config === null || config === void 0 ? void 0 : config.language)) {
            console_1.default.debug(`Exercise language ignored, instead imported from configuration ${config === null || config === void 0 ? void 0 : config.language}`);
            exercise.language = detected === null || detected === void 0 ? void 0 : detected.language;
        }
        else if ((detected === null || detected === void 0 ? void 0 : detected.language) &&
            (!(config === null || config === void 0 ? void 0 : config.language) || (config === null || config === void 0 ? void 0 : config.language) === "auto")) {
            console_1.default.debug(`Switching to ${detected.language} engine in this exercise`);
            exercise.language = detected.language;
        }
        // WARNING: has to be the FULL PATH to the entry path
        // We need to detect entry in both gradings: Incremental and Isolate
        exercise.entry = detected === null || detected === void 0 ? void 0 : detected.entry;
        console_1.default.debug(`Exercise detected entry: ${detected === null || detected === void 0 ? void 0 : detected.entry} and language ${exercise.language}`);
        // exercises.graded and exercises.disableGrading deprecated.
        if (!exercise.graded ||
            (config === null || config === void 0 ? void 0 : config.disableGrading) ||
            ((_b = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _b === void 0 ? void 0 : _b.includes("test"))) {
            socket_1.default.removeAllowed("test");
        }
        else {
            socket_1.default.addAllowed("test");
        }
        if (!exercise.entry || ((_c = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _c === void 0 ? void 0 : _c.includes("build"))) {
            socket_1.default.removeAllowed("build");
        }
        else {
            socket_1.default.addAllowed("build");
        }
        if (exercise.files.filter((f) => !f.name.toLowerCase().includes("readme.") &&
            !f.name.toLowerCase().includes("test.")).length === 0 ||
            ((_d = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _d === void 0 ? void 0 : _d.includes("reset"))) {
            socket_1.default.removeAllowed("reset");
        }
        else if (!((_e = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _e === void 0 ? void 0 : _e.includes("reset"))) {
            socket_1.default.addAllowed("reset");
        }
        socket_1.default.log("ready");
        res.json(exercise);
    }));
    app.get("/exercise/:slug/file/:fileName", withHandler((req, res) => {
        const exercise = configManager.getExercise(req.params.slug);
        if (exercise && exercise.getFile) {
            res.write(exercise.getFile(req.params.fileName));
            res.end();
        }
    }));
    app.get("/sidebar", withHandler((req, res) => {
        const sidebar = configManager.getSidebar();
        res.json(sidebar);
    }));
    app.delete("/exercise/:slug/delete", withHandler(async (req, res) => {
        const exerciseDeleted = configManager.deleteExercise(req.params.slug);
        if (exerciseDeleted) {
            configManager.buildIndex();
            res.json({ status: "ok" });
        }
        else {
            res.status(500).json({ error: "Failed to delete exercise" });
        }
    }));
    app.post("/actions/translate", jsonBodyParser, withHandler(async (req, res) => {
        var _a;
        const { exerciseSlugs, languages } = req.body;
        const session = await session_1.default.getPayload();
        const rigoToken = (_a = session === null || session === void 0 ? void 0 : session.rigobot) === null || _a === void 0 ? void 0 : _a.key;
        const configDirPath = config === null || config === void 0 ? void 0 : config.dirPath;
        if (!rigoToken) {
            return res.status(400).json({ error: "RigoToken not found" });
        }
        const languagesToTranslate = languages.split(",");
        try {
            await Promise.all(exerciseSlugs.map(async (slug) => {
                const exercise = configManager.getExercise(slug);
                if (!exercise) {
                    throw new Error(`Exercise ${slug} not found`);
                }
                if (exercise.getReadme) {
                    const readme = exercise.getReadme(null);
                    await Promise.all(languagesToTranslate.map(async (language) => {
                        const response = await (0, rigoActions_1.translateExercise)(rigoToken, {
                            text_to_translate: readme.body,
                            output_language: language,
                            exercise_slug: slug,
                        });
                        await (0, creatorUtilities_1.saveTranslatedReadme)(slug, response.parsed.output_language_code, response.parsed.translation);
                        (0, sidebarGenerator_1.addExerciseToSidebar)(slug, response.parsed.output_language_code, response.parsed.translated_slug, configDirPath || "");
                        console_1.default.success(`Translated ${slug} to ${language} successfully`);
                    }));
                }
            }));
            configManager.buildIndex();
            return res.status(200).json({ message: "Translated exercises" });
        }
        catch (error) {
            console.log(error, "ERROR");
            return res.status(400).json({ error: error.message });
        }
    }));
    app.put("/actions/rename", jsonBodyParser, withHandler(async (req, res) => {
        const { slug, newSlug } = req.body;
        const exercise = configManager.getExercise(slug);
        if (!exercise) {
            return res.status(400).json({ error: "Exercise not found" });
        }
        try {
            if (exercise.renameFolder) {
                exercise.renameFolder(newSlug);
                res.json({ status: "ok" });
            }
            else {
                res.status(500).json({
                    error: "Failed to rename exercise because it's not supported by the exercise",
                });
            }
        }
        catch (_a) {
            res.status(500).json({ error: "Failed to rename exercise" });
        }
    }));
    app.post("/exercise/:slug/create", jsonBodyParser, withHandler(async (req, res) => {
        const { title, readme, language } = req.body;
        const { slug } = req.params;
        if (!title || !readme || !language) {
            return res.status(400).json({ error: "Missing required fields" });
        }
        try {
            const exerciseCreated = await configManager.createExercise(slug, readme, language);
            if (exerciseCreated) {
                configManager.buildIndex();
                res.json({ status: "ok" });
            }
            else {
                res.status(500).json({ error: "Failed to create exercise" });
            }
        }
        catch (_a) {
            res.status(500).json({ error: "Failed to create exercise" });
        }
    }));
    const textBodyParser = bodyParser.text();
    app.put("/exercise/:slug/file/:fileName", textBodyParser, withHandler((req, res) => {
        const exercise = configManager.getExercise(req.params.slug);
        if (exercise && exercise.saveFile) {
            exercise.saveFile(req.params.fileName, req.body);
            res.end();
        }
    }));
    if (config === null || config === void 0 ? void 0 : config.outputPath) {
        app.use("/preview", express.static(config.outputPath));
    }
    app.use("/", express.static(`${config === null || config === void 0 ? void 0 : config.dirPath}/_app`));
    app.use("/creator", express.static(path.join(__dirname, "..", "creatorDist")));
}
