"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("@oclif/command");
const BaseCommand_1 = require("../utils/BaseCommand");
const ora_1 = require("ora");
// eslint-disable-next-line
const fs = require("fs-extra");
const prompts = require("prompts");
const cli_ux_1 = require("cli-ux");
const eta = require("eta");
const api_1 = require("../utils/api");
const console_1 = require("../utils/console");
const errors_1 = require("../utils/errors");
const path = require("path");
const rigoActions_1 = require("../utils/rigoActions");
const api_2 = require("../utils/api");
const creatorUtilities_1 = require("../utils/creatorUtilities");
const session_1 = require("../managers/session");
const durationByKind = {
    code: 5,
    quiz: 3,
    read: 2,
};
function estimateActivities(estimatedDuration) {
    const result = {};
    for (const kind of Object.keys(durationByKind)) {
        result[kind] = Math.floor(estimatedDuration / durationByKind[kind]);
    }
    return result;
}
const PARAMS = {
    expected_grade_level: "6",
    max_fkgl: 8,
    max_words: 200,
    max_rewrite_attempts: 3,
    max_title_length: 50,
};
const whichTargetAudienceAndEstimatedDuration = async () => {
    const res = await prompts([
        {
            type: "text",
            name: "targetAudience",
            message: "What is the target audience for this tutorial?",
        },
        {
            type: "number",
            name: "estimatedDuration",
            message: "What is the estimated duration for this tutorial in minutes?",
        },
    ]);
    return {
        targetAudience: res.targetAudience,
        estimatedDuration: res.estimatedDuration,
    };
};
async function processExercise(rigoToken, steps, packageContext, exercise, exercisesDir) {
    const { exNumber, exTitle, kind, description } = (0, creatorUtilities_1.getExInfo)(exercise);
    const exerciseDir = path.join(exercisesDir, `${exNumber}-${exTitle}`);
    const readme = await (0, rigoActions_1.readmeCreator)(rigoToken, {
        title: `${exNumber} - ${exTitle}`,
        output_lang: "en",
        list_of_exercises: steps.join(","),
        tutorial_description: packageContext,
        lesson_description: description,
        kind: kind.toLowerCase(),
        last_lesson: "",
    }, "learnpack-lesson-writer");
    const duration = durationByKind[kind.toLowerCase()];
    let attempts = 0;
    let readability = (0, creatorUtilities_1.checkReadability)(readme.parsed.content, 200, duration || 1);
    while (readability.fkglResult.fkgl > PARAMS.max_fkgl &&
        attempts < PARAMS.max_rewrite_attempts) {
        console_1.default.warning(`The lesson ${exTitle} has as readability score of  ${readability.fkglResult.fkgl} . It exceeds the maximum of words per minute. Rewriting it... (Attempt ${attempts + 1})`);
        // eslint-disable-next-line
        const reducedReadme = await (0, rigoActions_1.makeReadmeReadable)(rigoToken, {
            lesson: readability.body,
            number_of_words: readability.minutes.toString(),
            expected_number_words: PARAMS.max_words.toString(),
            fkgl_results: JSON.stringify(readability.fkglResult),
            expected_grade_level: PARAMS.expected_grade_level,
        }, "learnpack-lesson-writer");
        // console.log("REDUCED README START", reducedReadme, "REDUCED README END")
        if (!reducedReadme)
            break;
        readability = (0, creatorUtilities_1.checkReadability)(reducedReadme.parsed.content, PARAMS.max_words, duration || 1);
        attempts++;
    }
    console_1.default.success(`After ${attempts} attempts, the lesson ${exTitle} has a readability score of ${readability.fkglResult.fkgl} using FKGL. And it has ${readability.minutes.toFixed(2)} minutes of reading time.`);
    const readmeFilename = "README.md";
    fs.writeFileSync(path.join(exerciseDir, readmeFilename), readability.newMarkdown);
    if (kind.toLowerCase() === "code") {
        const codeFile = await (0, rigoActions_1.createCodeFile)(rigoToken, {
            readme: readability.newMarkdown,
            tutorial_info: packageContext,
        });
        fs.writeFileSync(path.join(exerciseDir, `app.${codeFile.parsed.extension.replace(".", "")}`), codeFile.parsed.content);
    }
    return readability.newMarkdown;
}
const initializeInteractiveCreation = async (rigoToken, courseInfo) => {
    let prevInteractions = "";
    let isReady = false;
    let currentSteps = [];
    let currentTitle = "";
    let currentDescription = "";
    let currentDifficulty = "";
    while (!isReady) {
        const spinner = (0, ora_1.default)("Thinking...").start();
        let wholeInfo = courseInfo;
        wholeInfo += `
    Current title: ${currentTitle}
    Current description: ${currentDescription}
    `;
        // eslint-disable-next-line
        const res = await (0, rigoActions_1.interactiveCreation)(rigoToken, {
            courseInfo: wholeInfo,
            prevInteractions: prevInteractions,
        });
        spinner.succeed("Done!");
        currentSteps = res.parsed.listOfSteps;
        isReady = res.parsed.ready;
        if (res.parsed.title && currentTitle !== res.parsed.title) {
            currentTitle = res.parsed.title;
        }
        if (res.parsed.description &&
            currentDescription !== res.parsed.description) {
            currentDescription = res.parsed.description;
        }
        if (res.parsed.difficulty && currentDifficulty !== res.parsed.difficulty) {
            currentDifficulty = res.parsed.difficulty;
        }
        if (!isReady) {
            console.log(currentSteps);
            console_1.default.info(`AI: ${res.parsed.aiMessage}`);
            prevInteractions += `\nAI: ${res.parsed.aiMessage}`;
            // eslint-disable-next-line
            const userMessage = await prompts([
                {
                    type: "text",
                    name: "userMessage",
                    message: "Your message: ",
                },
            ]);
            prevInteractions += `\nUser: ${userMessage.userMessage}`;
        }
    }
    const duration = (0, creatorUtilities_1.estimateDuration)(currentSteps);
    return {
        steps: currentSteps,
        title: currentTitle,
        description: currentDescription,
        interactions: prevInteractions,
        difficulty: currentDifficulty,
        duration,
    };
};
process.emitWarning = (warning) => {
    console_1.default.debug("A Warning was emitted by Node.js: ", warning);
};
const handleAILogic = async (tutorialDir, packageInfo) => {
    fs.removeSync(path.join(tutorialDir, "exercises", "01-hello-world"));
    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=4geeks-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_2.getConsumable)(sessionPayload.token, "ai-generation");
    if (consumable.count === 0) {
        console_1.default.error("It seems you cannot generate tutorials with AI. 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 isCreator = await hasCreatorPermission(rigoToken)
    // if (!isCreator) {
    //   Console.error(
    //     "👀 Oops! You need to be a creator to use our specialized AI. Please contact support"
    //   )
    //   process.exit(1)
    // }
    console_1.default.success("🎉 Let's begin this learning journey!");
    const { targetAudience, estimatedDuration } = await whichTargetAudienceAndEstimatedDuration();
    const contentIndex = await (0, creatorUtilities_1.appendContentIndex)();
    const airules = await (0, creatorUtilities_1.appendAIRules)();
    if (airules) {
        const rulesDir = path.join(tutorialDir, ".learn", "rules");
        // Crear el directorio si no existe
        fs.mkdirSync(rulesDir, { recursive: true });
        fs.writeFileSync(path.join(rulesDir, "airules.txt"), airules);
    }
    let packageContext = `
  \n
  Title: "${packageInfo.title.us}"
  Description: "${packageInfo.description.us}"
  Target Audience: "${targetAudience}"
  Estimated Duration: "${estimatedDuration} minutes"

  ${contentIndex ?
        `Content Index submitted by the user, use this to guide your creation. Keep in mind that your tutorial should contain these topics: 
      ---
      ${contentIndex}
      ---
      ` :
        ""}

  This is the duration for each type of step, use it to estimate the number of steps to create:
  ${Object.entries(durationByKind)
        .map(([key, value]) => `${key}: ${value} minutes`)
        .join("\n")}


  Within the estimated duration, is possible to have the following activities:
  Format=
  Activity: Maximum number of steps for duration

  Estimated activities:
  ${JSON.stringify(estimateActivities(estimatedDuration))}

  You should create a tutorial that is engaging and fun to follow.


  ${airules ?
        `
      This is a list of rules you need to follow when creating the tutorial:
      ${airules}
      ` :
        ""}
  `;
    const { steps, title, description, duration, difficulty } = await initializeInteractiveCreation(rigoToken, packageContext);
    packageInfo.title.us = title;
    packageInfo.description.us = description;
    packageInfo.duration = duration;
    packageInfo.difficulty = difficulty;
    packageContext = `
  Title: "${title}"
  Description: "${description}"
  Target Audience: "${targetAudience}"
  List of exercises: ${steps.join(", ")}

  AI Rules: ${airules}
  `;
    const exercisesDir = path.join(tutorialDir, "exercises");
    fs.ensureDirSync(exercisesDir);
    console_1.default.info("Creating lessons...");
    for (const [index, exercise] of steps.entries()) {
        const { exNumber, exTitle } = (0, creatorUtilities_1.getExInfo)(exercise);
        const exerciseDir = path.join(exercisesDir, `${exNumber}-${exTitle}`);
        fs.ensureDirSync(exerciseDir);
    }
    const exercisePromises = steps.map(exercise => processExercise(rigoToken, steps, packageContext, exercise, exercisesDir));
    const readmeContents = await Promise.all(exercisePromises);
    console_1.default.success("Lessons created! 🎉");
    console_1.default.info("Generating images for the lessons...");
    let imagesArray = [];
    for (const content of readmeContents) {
        imagesArray = [...imagesArray, ...(0, creatorUtilities_1.extractImagesFromMarkdown)(content)];
    }
    const imagePromises = imagesArray.map(async (image) => {
        try {
            const filename = (0, creatorUtilities_1.getFilenameFromUrl)(image.url);
            const webhookUrl = `${process.env.HOST}/webhooks/${(0, creatorUtilities_1.slugify)(packageInfo.title.us)}/images/${filename}`;
            const imagePath = path.join(tutorialDir, ".learn", "assets", filename);
            const res = await (0, rigoActions_1.generateImage)(rigoToken, {
                prompt: image.alt,
                callbackUrl: webhookUrl,
            });
            await (0, rigoActions_1.downloadImage)(res.image_url, imagePath);
            return true;
        }
        catch (_a) {
            console_1.default.error(`Error downloading image ${image.url}`);
            return false;
        }
    });
    await Promise.all(imagePromises);
    console_1.default.info("Images generated successfully! 🎉 Your tutorial will be ready soon!");
    console_1.default.info("Creating preview readme...");
    await (0, rigoActions_1.createPreviewReadme)(tutorialDir, packageInfo, rigoToken, readmeContents);
    const imagePath = path.join(tutorialDir, "preview.png");
    const res = await (0, rigoActions_1.generateImage)(rigoToken, {
        prompt: "Generate a preview image for the tutorial. This is all the tutorial information: " +
            packageContext +
            "\n Generate only a basic preview image, add the tutorial Title as a text add the top middle, avoid adding any other text elements. Try to generate an image that related with the tutorial content.",
        callbackUrl: `${process.env.HOST}/webhooks/${(0, creatorUtilities_1.slugify)(packageInfo.title.us)}/images/preview.png`,
    });
    // await downloadImage(res.image_url, imagePath)
    return true;
};
const getChoices = async (empty) => {
    const defaultChoices = {
        title: "My Interactive Tutorial",
        description: "",
        difficulty: "beginner",
        duration: 5,
        useAI: "no",
        grading: "isolated",
    };
    if (empty) {
        return defaultChoices;
    }
    const choices = await prompts([
        {
            type: "select",
            name: "grading",
            message: "How are you going to grade students or yourself?",
            choices: [
                {
                    title: "No grading: No feedback or testing whatsoever, similar to a an interactive book.",
                    value: null,
                },
                {
                    title: "Isolated: Each step is a new separate exercise",
                    value: "isolated",
                },
                {
                    title: "Step by step: Each step builds on top of each other like an incremental tutorial",
                    value: "incremental",
                },
            ],
        },
        {
            type: "text",
            name: "title",
            initial: "My Interactive Tutorial",
            message: "Title for your tutorial? Press enter to leave as it is",
            validate: (value) => {
                if (value.length > PARAMS.max_title_length) {
                    return `Title must be less than ${PARAMS.max_title_length} characters`;
                }
                return true;
            },
        },
        {
            type: "text",
            name: "description",
            initial: "",
            message: "Description for your tutorial? Press enter to leave blank",
        },
        {
            type: "select",
            name: "useAI",
            message: "Want a little bit of AI magic to help you? Our AI can craft the tutorial for you",
            choices: [
                {
                    title: "Yes, please help me",
                    value: "yes",
                },
                { title: "No, thanks, I prefer to do it manually", value: "no" },
            ],
        },
    ]);
    const completeChoices = Object.assign(Object.assign({}, choices), { difficulty: "beginner", duration: 30 });
    return completeChoices;
};
class InitComand extends BaseCommand_1.default {
    async run() {
        const { flags } = this.parse(InitComand);
        await alreadyInitialized();
        const choices = await getChoices(flags.yes);
        const packageInfo = (0, creatorUtilities_1.makePackageInfo)(choices);
        const tutorialDir = `./${packageInfo.slug}`;
        fs.ensureDirSync(tutorialDir);
        const templatesDir = path.resolve(__dirname, "../../src/utils/templates/" + (choices.grading || "no-grading"));
        if (!fs.existsSync(templatesDir))
            throw (0, errors_1.ValidationError)(`Template ${templatesDir} does not exists`);
        await fs.copySync(templatesDir, tutorialDir);
        if (choices.useAI === "yes") {
            await handleAILogic(tutorialDir, packageInfo);
        }
        const languages = ["en", "es"];
        // Creating README files
        for (const language of languages) {
            const readmeFilename = `README${language !== "en" ? `.${language}` : ""}`;
            const readmeTemplatePath = path.resolve(templatesDir, `${readmeFilename}.ejs`);
            if (choices.useAI !== "yes") {
                const readmeObject = {
                    title: packageInfo.title.us,
                    description: packageInfo.description.us,
                    grading: packageInfo.grading,
                    difficulty: packageInfo.difficulty,
                    duration: packageInfo.duration,
                };
                const readmeContent = eta.render(fs.readFileSync(readmeTemplatePath, "utf-8"), readmeObject);
                fs.writeFileSync(path.join(tutorialDir, `${readmeFilename}.md`), readmeContent);
            }
            if (fs.existsSync(path.join(tutorialDir, `${readmeFilename}.ejs`)))
                fs.removeSync(path.join(tutorialDir, `${readmeFilename}.ejs`));
        }
        cli_ux_1.default.action.start("Initializing package");
        if (!fs.existsSync(path.join(tutorialDir, ".gitignore")))
            fs.copyFile(path.resolve(__dirname, "../../src/utils/templates/gitignore.txt"), path.join(tutorialDir, ".gitignore"));
        fs.writeFileSync(path.join(tutorialDir, "learn.json"), JSON.stringify(packageInfo, null, 2));
        cli_ux_1.default.action.stop();
        console_1.default.success(`😋 Package initialized successfully in ${tutorialDir}`);
        console_1.default.help(`Get inside the tutorial with the command: $ cd ${tutorialDir}`);
        console_1.default.help(`Start the exercises by running the following command on your terminal: $ learnpack start`);
        process.exit(0);
    }
}
InitComand.description = "Create a new learning package: Book, Tutorial or Exercise";
InitComand.flags = Object.assign(Object.assign({}, BaseCommand_1.default.flags), { grading: command_1.flags.help({ char: "h" }) });
const alreadyInitialized = () => new Promise((resolve, reject) => {
    fs.readdir("./", function (err, files) {
        files = files.filter((f) => [".learn", "learn.json", "bc.json", ".breathecode"].includes(f));
        if (err) {
            reject((0, errors_1.ValidationError)(err.message));
            throw (0, errors_1.ValidationError)(err.message);
        }
        else if (files.length > 0) {
            reject((0, errors_1.ValidationError)("It seems the package is already initialized because we've found the following files: " +
                files.join(",")));
            throw (0, errors_1.ValidationError)("It seems the package is already initialized because we've found the following files: " +
                files.join(","));
        }
        resolve(false);
    });
});
exports.default = InitComand;
