"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleAssetCreation = void 0;
/* eslint-disable arrow-parens */
/* eslint-disable unicorn/no-array-for-each */
const child_process_1 = require("child_process");
const command_1 = require("@oclif/command");
const SessionCommand_1 = require("../utils/SessionCommand");
const session_1 = require("../managers/session");
const fs = require("fs");
const path = require("path");
const archiver = require("archiver");
const axios_1 = require("axios");
const FormData = require("form-data");
const console_1 = require("../utils/console");
const file_1 = require("../managers/file");
const api_1 = require("../utils/api");
const prompts = require("prompts");
const rigoActions_1 = require("../utils/rigoActions");
const misc_1 = require("../utils/misc");
const creatorUtilities_1 = require("../utils/creatorUtilities");
const uploadZipEndpont = api_1.RIGOBOT_HOST + "/v1/learnpack/upload";
const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, all_translations = []) => {
    const categories = {
        en: 9,
        us: 9,
        es: 10,
    };
    let category = categories[selectedLang];
    if (!category) {
        category = 91;
    }
    try {
        const user = await api_1.default.validateToken(sessionPayload.token);
        const slug = (0, creatorUtilities_1.slugify)(learnJson.title[selectedLang]).slice(0, 50);
        const { exists } = await api_1.default.doesAssetExists(sessionPayload.token, slug);
        if (!exists) {
            console_1.default.info("Asset does not exist in this academy, creating it");
            const asset = await api_1.default.createAsset(sessionPayload.token, {
                slug: slug,
                title: learnJson.title[selectedLang],
                lang: selectedLang,
                description: learnJson.description[selectedLang],
                learnpack_deploy_url: learnpackDeployUrl,
                technologies: learnJson.technologies,
                url: learnpackDeployUrl,
                category: category,
                owner: user.id,
                author: user.id,
                preview: learnJson.preview,
                readme_raw: b64IndexReadme,
                all_translations,
            });
            await api_1.default.updateRigoAssetID(sessionPayload.token, learnJson.slug, asset.id);
            console_1.default.info("Asset created with id", asset.id);
            return asset;
        }
        console_1.default.info("Asset exists, updating it");
        const asset = await api_1.default.updateAsset(sessionPayload.token, slug, {
            learnpack_deploy_url: learnpackDeployUrl,
            title: learnJson.title[selectedLang],
            description: learnJson.description[selectedLang],
            all_translations,
        });
        await api_1.default.updateRigoAssetID(sessionPayload.rigobotToken.trim(), learnJson.slug, asset.id);
        console_1.default.info("Asset updated with id", asset.id);
        return asset;
    }
    catch (error) {
        console_1.default.error("Error updating or creating asset:", error);
        return null;
    }
};
exports.handleAssetCreation = handleAssetCreation;
const runAudit = (strict) => {
    try {
        console_1.default.info("Running learnpack audit before publishing...");
        (0, child_process_1.execSync)(`learnpack audit ${strict ? "--strict" : ""}`, {
            stdio: "inherit",
        });
    }
    catch (error) {
        console_1.default.error("Failed to audit with learnpack");
        // Si `execSync` lanza un error, capturamos el mensaje de error
        if (error instanceof Error) {
            console_1.default.error(error.message);
        }
        else {
            console_1.default.error("Unknown error occurred");
        }
        // Detener la ejecución del comando si `learnpack publish` falla
        process.exit(1);
    }
    // Continuar con el proceso de build solo si `learnpack publish` fue exitoso
    console_1.default.info("Learnpack publish completed successfully. Proceeding with build...");
};
function getCategoriesByAcademy(categories, academy) {
    return categories.filter((cat) => cat.academy.id === academy.id);
}
const selectAcademy = async (academies, bcToken) => {
    if (academies.length === 0) {
        return { academy: null, category: 0 };
    }
    if (academies.length === 1) {
        return { academy: academies[0], category: 0 };
    }
    // prompts the user to  select an academy to upload the assets
    console_1.default.info("In which academy do you want to publish the asset?");
    const response = await prompts({
        type: "select",
        name: "academy",
        message: "Select an academy",
        choices: academies.map((academy) => ({
            title: academy.name,
            value: academy,
        })),
    });
    const categories = await api_1.default.getCategories(bcToken);
    const categoriesByAcademy = getCategoriesByAcademy(categories, response.academy);
    const categoriesPrompt = await prompts({
        type: "select",
        name: "category",
        message: "Select a category",
        choices: categoriesByAcademy.map((category) => ({
            title: category.title,
            value: category.id,
        })),
    });
    return {
        academy: response.academy,
        category: categoriesPrompt.category,
    };
};
class BuildCommand extends SessionCommand_1.default {
    async init() {
        const { flags } = this.parse(BuildCommand);
        await this.initSession(flags);
    }
    async run() {
        var _a, _b, _c;
        const buildDir = path.join(process.cwd(), "build");
        const { flags } = this.parse(BuildCommand);
        const strict = flags.strict;
        console_1.default.debug("Strict mode: ", strict);
        // this.configManager?.clean()
        const configObject = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.get();
        let sessionPayload = await session_1.default.getPayload();
        const sessionExists = sessionPayload && sessionPayload.rigobot;
        const isValidToken = sessionExists && sessionPayload.rigobot.key ?
            await (0, rigoActions_1.isValidRigoToken)(sessionPayload.rigobot.key) :
            false;
        const isValidBreathecodeToken = sessionExists && sessionPayload.token ?
            await api_1.default.validateToken(sessionPayload.token) :
            false;
        if (!sessionExists || !isValidBreathecodeToken || !isValidToken) {
            console_1.default.info("Almost there! First you need to login with 4Geeks.com to use AI Generation tool for creators. You can create a new account here: https://4geeks.com/checkout?plan=learnpack-creator");
            try {
                sessionPayload = await session_1.default.login();
            }
            catch (error) {
                console_1.default.error("Error trying to authenticate");
                console_1.default.error(error.message || error);
            }
        }
        const rigoToken = sessionPayload.rigobot.key;
        const consumable = await (0, api_1.getConsumable)(sessionPayload.token, "learnpack-publish");
        if (consumable.count === 0) {
            console_1.default.error("It seems you cannot publish tutorials. Make sure you creator subscription is up to date here: https://4geeks.com/profile/subscriptions. If you believe there is an issue you can always contact support@4geeks.com");
            process.exit(1);
        }
        if (configObject) {
            console_1.default.info("Cleaning configuration files");
            (_b = this.configManager) === null || _b === void 0 ? void 0 : _b.clean();
            // build exerises
            runAudit(strict);
            console_1.default.debug("Building exercises");
            (_c = this.configManager) === null || _c === void 0 ? void 0 : _c.buildIndex();
        }
        const academies = await api_1.default.listUserAcademies(sessionPayload.token);
        if (academies.length === 0) {
            console_1.default.error("It seems you cannot publish tutorials. Make sure you creator subscription is up to date here: https://4geeks.com/profile/subscriptions. If you believe there is an issue you can always contact support@4geeks.com");
            process.exit(1);
        }
        const { academy, category } = await selectAcademy(academies, sessionPayload.token);
        const learnJsonPath = path.join(process.cwd(), "learn.json");
        if (!fs.existsSync(learnJsonPath)) {
            this.error("learn.json not found");
        }
        const learnJson = JSON.parse(fs.readFileSync(learnJsonPath, "utf-8"));
        const zipFilePath = path.join(process.cwd(), `${learnJson.slug}.zip`);
        // Ensure build directory exists
        if (!fs.existsSync(buildDir)) {
            fs.mkdirSync(buildDir);
        }
        if (configObject) {
            const { config } = configObject;
            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/`);
            }
        }
        // Copy config.json
        const configPath = path.join(process.cwd(), ".learn", "config.json");
        if (fs.existsSync(configPath)) {
            fs.copyFileSync(configPath, path.join(buildDir, "config.json"));
        }
        else {
            this.error("config.json not found");
        }
        // Copy .learn/assets directory, if it exists else create it
        const assetsDir = path.join(process.cwd(), ".learn", "assets");
        if (fs.existsSync(assetsDir)) {
            this.copyDirectory(assetsDir, path.join(buildDir, ".learn", "assets"));
        }
        else {
            fs.mkdirSync(path.join(buildDir, ".learn", "assets"), { recursive: true });
        }
        // Copy .learn/_app directory files to the same level as config.json
        const appDir = path.join(process.cwd(), ".learn", "_app");
        if (fs.existsSync(appDir)) {
            this.copyDirectory(appDir, buildDir);
        }
        else {
            this.error(".learn/_app directory not found");
        }
        // After copying the _app directory
        const indexHtmlPath = path.join(appDir, "index.html");
        const buildIndexHtmlPath = path.join(buildDir, "index.html");
        const manifestPWA = path.join(appDir, "manifest.webmanifest");
        const buildManifestPWA = path.join(buildDir, "manifest.webmanifest");
        if (fs.existsSync(indexHtmlPath)) {
            let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8");
            const description = learnJson.description.en || "LearnPack is awesome!";
            const title = learnJson.title.en || "LearnPack: Interactive Learning as a Service";
            const previewUrl = learnJson.preview ||
                "https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/public/learnpack.svg";
            // Replace placeholders and the <title>Old title </title> tag for a new tag with the title
            indexHtmlContent = indexHtmlContent
                .replace(/{{description}}/g, description)
                .replace(/<title>.*<\/title>/, `<title>${title}</title>`)
                .replace(/{{title}}/g, title)
                .replace(/{{preview}}/g, previewUrl)
                .replace(/{{slug}}/g, learnJson.slug)
                .replace(/{{duration}}/g, (0, misc_1.minutesToISO8601Duration)(learnJson.duration));
            // Write the modified content to the build directory
            fs.writeFileSync(buildIndexHtmlPath, indexHtmlContent);
        }
        else {
            this.error("index.html not found in _app directory");
        }
        if (fs.existsSync(manifestPWA)) {
            let manifestPWAContent = fs.readFileSync(manifestPWA, "utf-8");
            manifestPWAContent = manifestPWAContent.replace("{{course_title}}", learnJson.title.us);
            const courseShortName = { answer: "testing-tutorial" };
            // const courseShortName = await generateCourseShortName(rigoToken, {
            //   learnJSON: JSON.stringify(learnJson),
            // })
            manifestPWAContent = manifestPWAContent.replace("{{course_app_name}}", courseShortName.answer);
            fs.writeFileSync(buildManifestPWA, manifestPWAContent);
        }
        else {
            this.error("manifest.webmanifest not found in _app directory");
        }
        // Copy exercises directory
        const exercisesDir = path.join(process.cwd(), "exercises");
        const learnExercisesDir = path.join(process.cwd(), ".learn", "exercises");
        if (fs.existsSync(exercisesDir)) {
            this.copyDirectory(exercisesDir, path.join(buildDir, "exercises"));
        }
        else if (fs.existsSync(learnExercisesDir)) {
            this.copyDirectory(learnExercisesDir, path.join(buildDir, "exercises"));
        }
        else {
            this.error("exercises directory not found in either location");
        }
        fs.copyFileSync(learnJsonPath, path.join(buildDir, "learn.json"));
        const sidebarPath = path.join(process.cwd(), ".learn", "sidebar.json");
        if (fs.existsSync(sidebarPath)) {
            fs.copyFileSync(sidebarPath, path.join(buildDir, "sidebar.json"));
        }
        else {
            this.error("sidebar.json not found in .learn directory");
        }
        const output = fs.createWriteStream(zipFilePath);
        const archive = archiver("zip", {
            zlib: { level: 9 },
        });
        output.on("close", async () => {
            this.log(`Build completed: ${zipFilePath} (${archive.pointer()} total bytes)`);
            // Remove build directory after zip is created
            console_1.default.debug("Zip file saved in project root");
            const formData = new FormData();
            formData.append("file", fs.createReadStream(zipFilePath));
            formData.append("config", JSON.stringify(learnJson));
            try {
                const res = await axios_1.default.post(uploadZipEndpont, formData, {
                    headers: Object.assign(Object.assign({}, formData.getHeaders()), { Authorization: `Token ${rigoToken}` }),
                });
                console.log(res.data);
                fs.unlinkSync(zipFilePath);
                this.removeDirectory(buildDir);
                await (0, exports.handleAssetCreation)(sessionPayload, learnJson, "en", res.data.url, "", []);
            }
            catch (error) {
                if (axios_1.default.isAxiosError(error)) {
                    if (error.response && error.response.status === 403) {
                        console.error("Error 403:", error.response.data.error);
                    }
                    else if (error.response && error.response.status === 400) {
                        console.error(error.response.data.error);
                    }
                    else {
                        console.error("Error uploading file:", error);
                    }
                }
                else {
                    console.error("Error uploading file:", error);
                }
                // fs.unlinkSync(zipFilePath)
                // this.removeDirectory(buildDir)
            }
        });
        archive.on("error", (err) => {
            throw err;
        });
        archive.pipe(output);
        archive.directory(buildDir, false);
        await archive.finalize();
    }
    copyDirectory(src, dest) {
        if (!fs.existsSync(dest)) {
            fs.mkdirSync(dest, { recursive: true });
        }
        const entries = fs.readdirSync(src, { withFileTypes: true });
        for (const entry of entries) {
            const srcPath = path.join(src, entry.name);
            const destPath = path.join(dest, entry.name);
            if (entry.isDirectory()) {
                this.copyDirectory(srcPath, destPath);
            }
            else {
                fs.copyFileSync(srcPath, destPath);
            }
        }
    }
    removeDirectory(dir) {
        if (fs.existsSync(dir)) {
            fs.readdirSync(dir).forEach((file) => {
                const currentPath = path.join(dir, file);
                if (fs.lstatSync(currentPath).isDirectory()) {
                    this.removeDirectory(currentPath);
                }
                else {
                    fs.unlinkSync(currentPath);
                }
            });
            fs.rmdirSync(dir);
        }
    }
}
BuildCommand.description = "Builds the project by copying necessary files and directories into a zip file";
BuildCommand.flags = {
    help: command_1.flags.help({ char: "h" }),
    strict: command_1.flags.boolean({
        char: "s",
        description: "strict mode",
        default: false,
    }),
};
BuildCommand.flags = {
    strict: command_1.flags.boolean({
        char: "s",
        description: "strict mode",
        default: false,
    }),
    help: command_1.flags.help({ char: "h" }),
};
exports.default = BuildCommand;
