"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// import path from "path";
const command_1 = require("@oclif/command");
const fs = require("fs");
const path = require("path");
const SessionCommand_1 = require("../utils/SessionCommand");
const console_1 = require("../utils/console");
const socket_1 = require("../managers/socket");
const telemetry_1 = require("../managers/telemetry");
const server_1 = require("../managers/server");
const fileQueue_1 = require("../utils/fileQueue");
const file_1 = require("../managers/file");
const misc_1 = require("../utils/misc");
const osOperations_1 = require("../utils/osOperations");
const checkNotInstalled_1 = require("../utils/checkNotInstalled");
const sidebarGenerator_1 = require("../utils/sidebarGenerator");
class StartCommand extends SessionCommand_1.default {
    // 🛑 IMPORTANT
    // Every command that will use the configManager needs this init method
    async init() {
        const { flags } = this.parse(StartCommand);
        await this.initSession(flags);
    }
    async run() {
        var _a, _b, _c, _d;
        // get configuration object
        const configObject = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.get();
        const hasXDG = await osOperations_1.eventManager.checkXDGInstalled();
        const installedPlugins = this.config.plugins.map(plugin => {
            return `${plugin.pjson.name}`;
        });
        if (configObject) {
            const { config } = configObject;
            // build exerises
            (_b = this.configManager) === null || _b === void 0 ? void 0 : _b.buildIndex();
            console_1.default.debug(`Grading: ${config === null || config === void 0 ? void 0 : config.grading} ${((_c = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _c === void 0 ? void 0 : _c.includes("test")) ? "(disabled)" : ""}, editor: ${config === null || config === void 0 ? void 0 : config.editor.mode} ${config === null || config === void 0 ? void 0 : config.editor.version}, for ${Array.isArray(configObject === null || configObject === void 0 ? void 0 : configObject.exercises) ?
                configObject === null || configObject === void 0 ? void 0 : configObject.exercises.length :
                0} exercises found`);
            (0, sidebarGenerator_1.generateSidebar)((configObject === null || configObject === void 0 ? void 0 : configObject.exercises) || [], (config === null || config === void 0 ? void 0 : config.dirPath) || "");
            const neededPlugins = await (0, checkNotInstalled_1.checkNotInstalledPlugins)((configObject === null || configObject === void 0 ? void 0 : configObject.exercises) || [], installedPlugins, this);
            const allDepsInstalled = await (0, checkNotInstalled_1.checkNotInstalledDependencies)(neededPlugins.needed);
            if (!allDepsInstalled) {
                this.exit(1);
            }
            const appAlreadyExists = (0, file_1.checkIfDirectoryExists)(`${config === null || config === void 0 ? void 0 : config.dirPath}/_app`);
            if (!appAlreadyExists) {
                // download app and decompress
                await (0, file_1.downloadEditor)(config === null || config === void 0 ? void 0 : config.editor.version, `${config === null || config === void 0 ? void 0 : config.dirPath}/app.tar.gz`);
                console_1.default.info("Decompressing LearnPack UI, this may take a minute...");
                await (0, file_1.decompress)(`${config === null || config === void 0 ? void 0 : config.dirPath}/app.tar.gz`, `${config === null || config === void 0 ? void 0 : config.dirPath}/_app/`);
            }
            // listen to socket commands
            if (config && this.configManager) {
                const server = await (0, server_1.default)(configObject, this.configManager, process.env.NODE_ENV === "test");
                server.setMaxListeners(30);
                // I should call a method to get the EventListener
                const dispatcher = fileQueue_1.default.dispatcher({
                    create: true,
                    path: `${config.dirPath}/vscode_queue.json`,
                });
                socket_1.default.start(config, server, false);
                const telemetryPath = ((_d = configObject.config) === null || _d === void 0 ? void 0 : _d.dirPath) || "";
                telemetry_1.default.start(telemetryPath, socket_1.default);
                socket_1.default.on("open", (data) => {
                    console_1.default.debug("Opening these files: ", data);
                    const files = (0, misc_1.prioritizeHTMLFile)(data.files);
                    if (config.editor.agent !== "os") {
                        // Console.info("Opening files for vscode agent")
                        osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_FILES, files);
                    }
                    else {
                        // dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
                        console_1.default.debug("Ignoring files for os agent");
                    }
                    socket_1.default.ready("Ready to compile...");
                });
                socket_1.default.on("open_window", (data) => {
                    console_1.default.debug("Opening window: ", data);
                    // cli.open(data.url); This uses XDG under the ground
                    if (config.os !== "linux" || (config.os === "linux" && hasXDG)) {
                        osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data);
                    }
                    else {
                        dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
                    }
                    socket_1.default.log("open_window", "", undefined, data.url);
                });
                socket_1.default.on("open_terminal", (exercise) => {
                    console_1.default.debug("Opening terminal: ", exercise);
                    // eventManager.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
                    if (config.editor.agent === "vscode") {
                        dispatcher.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
                    }
                });
                socket_1.default.on("reset", (exercise) => {
                    var _a;
                    try {
                        (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.reset(exercise.exerciseSlug);
                        socket_1.default.ready("Ready to compile...");
                    }
                    catch (error) {
                        socket_1.default.error("compiler-error", error.message ||
                            "There was an error reseting the exercise");
                        setTimeout(() => socket_1.default.ready("Ready to compile..."), 2000);
                    }
                });
                socket_1.default.on("build", async (data) => {
                    var _a;
                    const exercise = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.getExercise(data.exerciseSlug);
                    if (!(exercise === null || exercise === void 0 ? void 0 : exercise.language)) {
                        socket_1.default.error("compiler-error", "Impossible to detect language to build for " +
                            data.exerciseSlug +
                            "...");
                        return;
                    }
                    socket_1.default.log("compiling", "Building exercise " +
                        data.exerciseSlug +
                        " with " +
                        exercise.language +
                        "...");
                    await this.config.runHook("action", {
                        action: "compile",
                        socket: socket_1.default,
                        configuration: config,
                        exercise,
                        telemetry: telemetry_1.default,
                    });
                });
                socket_1.default.on("test", async (data) => {
                    var _a, _b;
                    const exercise = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.getExercise(data.exerciseSlug);
                    if (!(exercise === null || exercise === void 0 ? void 0 : exercise.language)) {
                        socket_1.default.error("compiler-error", "Impossible to detect engine language for testing for " +
                            data.exerciseSlug +
                            "...");
                        return;
                    }
                    if ((config === null || config === void 0 ? void 0 : config.disabledActions.includes("test")) ||
                        (config === null || config === void 0 ? void 0 : config.disableGrading)) {
                        socket_1.default.ready("Grading is disabled on configuration");
                        return true;
                    }
                    socket_1.default.log("testing", "Testing your exercise using the " + exercise.language + " engine.");
                    await this.config.runHook("action", {
                        action: "test",
                        socket: socket_1.default,
                        configuration: config,
                        exercise,
                        telemetry: telemetry_1.default,
                    });
                    try {
                        if (!configObject.config) {
                            return;
                        }
                        const getReportPath = (ext) => {
                            var _a;
                            if (!((_a = configObject.config) === null || _a === void 0 ? void 0 : _a.dirPath)) {
                                throw new Error("No directory path found in config");
                            }
                            return path.join(configObject.config.dirPath, "reports", `${exercise.slug}`, `report.${ext}`);
                        };
                        const markdownReportPath = getReportPath("md");
                        const textReportPath = getReportPath("txt");
                        if (fs.existsSync(markdownReportPath)) {
                            let reportContent = "";
                            reportContent = fs.readFileSync(markdownReportPath, "utf8");
                            socket_1.default.dialog(reportContent);
                        }
                        if (fs.existsSync(textReportPath)) {
                            let reportContent = "";
                            reportContent = fs.readFileSync(textReportPath, "utf8");
                            socket_1.default.dialog(reportContent);
                        }
                    }
                    catch (error) {
                        console_1.default.debug("Error finding report for exercise.slug", error);
                    }
                    (_b = this.configManager) === null || _b === void 0 ? void 0 : _b.save();
                    return true;
                });
                const terminate = async () => {
                    var _a;
                    console_1.default.error("Terminating Learnpack...");
                    (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.noCurrentExercise();
                    dispatcher.enqueue(dispatcher.events.END);
                    process.exit();
                };
                server.on("close", terminate);
                process.on("SIGINT", terminate);
                process.on("SIGTERM", terminate);
                process.on("SIGHUP", terminate);
                // finish the server startup
                setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000);
                // start watching for file changes
                if (StartCommand.flags.watch)
                    this.configManager.watchIndex((_filename, _fileContent) => {
                        // Instead of reloading with socket.reload(), I just notify the frontend for the file change
                        socket_1.default.emit("file_change", "ready", [_filename, _fileContent]);
                    });
            }
        }
    }
}
StartCommand.description = "Runs a small server with all the exercise instructions";
StartCommand.flags = Object.assign(Object.assign({}, SessionCommand_1.default.flags), { port: command_1.flags.string({ char: "p", description: "server port" }), host: command_1.flags.string({ char: "h", description: "server host" }), disableGrading: command_1.flags.boolean({
        char: "D",
        description: "disble grading functionality",
        default: false,
    }), 
    // disableGrading: flags.boolean({char: 'dg', description: 'disble grading functionality', default: false }),
    watch: command_1.flags.boolean({
        char: "w",
        description: "Watch for file changes",
        default: false,
    }), editor: command_1.flags.string({
        char: "e",
        description: "[preview, extension]",
        options: ["extension", "preview"],
    }), version: command_1.flags.string({
        char: "v",
        description: "E.g: 1.0.1",
        default: undefined,
    }), grading: command_1.flags.string({
        char: "g",
        description: "[isolated, incremental]",
        options: ["isolated", "incremental"],
    }), debug: command_1.flags.boolean({
        char: "d",
        description: "debugger mode for more verbage",
        default: false,
    }) });
exports.default = StartCommand;
