"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processImage = exports.createLearnJson = void 0;
const tslib_1 = require("tslib");
const command_1 = require("@oclif/command");
const buffer_1 = require("buffer");
const express = require("express");
const cors = require("cors");
const path = require("path");
const http = require("http");
const creatorSocket_1 = require("../utils/creatorSocket");
const os = require("os");
const archiver = require("archiver");
const mkdirp = require("mkdirp");
const html_to_text_1 = require("html-to-text");
const rimraf = require("rimraf");
const SessionCommand_1 = require("../utils/SessionCommand");
const storage_1 = require("@google-cloud/storage");
const file_1 = require("../managers/file");
const fs = require("fs");
const rigoActions_1 = require("../utils/rigoActions");
const dotenv = require("dotenv");
// import { v4 as uuidv4 } from "uuid"
const creatorUtilities_1 = require("../utils/creatorUtilities");
// import { handleAssetCreation } from "./publish"
const axios_1 = require("axios");
const FormData = require("form-data");
const api_1 = require("../utils/api");
const misc_1 = require("../utils/misc");
const configBuilder_1 = require("../utils/configBuilder");
const creatorUtilities_2 = require("../utils/creatorUtilities");
const sidebarGenerator_1 = require("../utils/sidebarGenerator");
const publish_1 = require("./publish");
const export_1 = require("../utils/export");
const frontMatter = require("front-matter");
dotenv.config();
// Asegúrate de tener uuid instalado
// npm install uuid
function findLast(array, predicate) {
    for (let i = array.length - 1; i >= 0; i--) {
        if (predicate(array[i]))
            return array[i];
    }
    return undefined;
}
const createLearnJson = (courseInfo) => {
    // console.log("courseInfo to create learn json", courseInfo)
    const expectedPreviewUrl = `https://${courseInfo.slug}.learn-pack.com/preview.png`;
    const language = courseInfo.language || "en";
    const learnJson = {
        slug: courseInfo.slug,
        title: {
            [language]: courseInfo.title,
        },
        technologies: courseInfo.technologies || [],
        difficulty: "beginner",
        description: {
            [language]: courseInfo.description,
        },
        grading: "isolated",
        telemetry: {
            batch: "https://breathecode.herokuapp.com/v1/assignment/me/telemetry",
        },
        preview: expectedPreviewUrl,
    };
    return learnJson;
};
exports.createLearnJson = createLearnJson;
const uploadFileToBucket = async (bucket, file, path) => {
    const fileRef = bucket.file(path);
    await fileRef.save(buffer_1.Buffer.from(file, "utf8"));
};
const PARAMS = {
    expected_grade_level: "8",
    max_fkgl: 10,
    max_words: 250,
    max_rewrite_attempts: 2,
    max_title_length: 50,
};
async function fetchComponentsYml() {
    try {
        const [assessmentResponse, explanatoryResponse] = await Promise.all([
            axios_1.default.get("https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/docs/assessment_components.yml"),
            axios_1.default.get("https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/docs/explanatory_components.yml"),
        ]);
        const combinedContent = `
# ASSESSMENT COMPONENTS
These components are designed for evaluation and knowledge assessment:

${assessmentResponse.data}

---

# EXPLANATORY COMPONENTS  
These components are designed for explanation and learning support:

${explanatoryResponse.data}
`;
        return combinedContent;
    }
    catch (error) {
        console.error("Failed to fetch components YAML files:", error);
        return "";
    }
}
const processImage = async (url, description, rigoToken, courseSlug) => {
    try {
        const filename = (0, creatorUtilities_1.getFilenameFromUrl)(url);
        const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/images/${filename}`;
        await (0, rigoActions_1.generateImage)(rigoToken, {
            prompt: description,
            callbackUrl: webhookUrl,
        });
        return true;
    }
    catch (_a) {
        return false;
    }
};
exports.processImage = processImage;
const createInitialSidebar = async (slugs, initialLanguage = "en") => {
    const sidebar = {};
    for (const slug of slugs) {
        sidebar[slug] = {
            [initialLanguage]: slug,
        };
    }
    return sidebar;
};
const uploadInitialReadme = async (bucket, exSlug, targetDir, packageContext) => {
    const isGeneratingText = `
  \`\`\`loader slug="${exSlug}"
  :rigo
  \`\`\`
      `;
    const readmeFilename = `README${(0, creatorUtilities_1.getReadmeExtension)(packageContext.language || "en")}`;
    await uploadFileToBucket(bucket, isGeneratingText, `${targetDir}/${readmeFilename}`);
};
const cleanFormState = (formState) => {
    const { description, technologies, purpose, hasContentIndex, duration, isCompleted, variables, currentStep, language } = formState, rest = tslib_1.__rest(formState, ["description", "technologies", "purpose", "hasContentIndex", "duration", "isCompleted", "variables", "currentStep", "language"]);
    return rest;
};
const cleanFormStateForSyllabus = (formState) => {
    return Object.assign(Object.assign({}, formState), { description: formState.description, technologies: formState.technologies, purposse: undefined, duration: undefined, hasContentIndex: undefined, variables: undefined, currentStep: undefined, language: undefined, isCompleted: undefined });
};
const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, courseJson, deployUrl) => {
    const availableLangs = Object.keys(courseJson.title);
    console.log("AVAILABLE LANGUAGES to upload asset", availableLangs);
    const all_translations = [];
    for (const lang of availableLangs) {
        // eslint-disable-next-line no-await-in-loop
        const indexReadme = await bucket.file(`courses/${courseSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(lang)}`);
        let indexReadmeString = "";
        try {
            // eslint-disable-next-line no-await-in-loop
            const [indexReadmeContent] = await indexReadme.download();
            indexReadmeString = indexReadmeContent.toString();
        }
        catch (error) {
            console.error("Error downloading index readme", error);
            // TODO: Trigger generation of the index readme
            indexReadmeString = "";
        }
        const b64IndexReadme = buffer_1.Buffer.from(indexReadmeString).toString("base64");
        // eslint-disable-next-line no-await-in-loop
        const asset = await (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken.trim() }, courseJson, lang, deployUrl, b64IndexReadme, all_translations);
        if (!asset) {
            console.log("No se pudo crear el asset, saltando idioma", lang);
            continue;
        }
        all_translations.push(asset.slug);
    }
};
async function startExerciseGeneration(rigoToken, steps, packageContext, exercise, courseSlug, purposeSlug, lastLesson = "") {
    const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
    console.log("Starting generation of", exSlug);
    const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/exercise-processor/${exercise.id}/${rigoToken}`;
    const res = await (0, rigoActions_1.readmeCreator)(rigoToken.trim(), {
        title: `${exercise.id} - ${exercise.title}`,
        output_lang: packageContext.language || "en",
        list_of_exercises: JSON.stringify(steps.map(step => step.id + "-" + step.title)),
        tutorial_description: JSON.stringify(cleanFormState(packageContext)),
        lesson_description: exercise.description,
        kind: exercise.type.toLowerCase(),
        last_lesson: lastLesson,
    }, purposeSlug, webhookUrl);
    console.log("README CREATOR RES", res);
    return res.id;
}
const lessonCleaner = (lesson) => {
    return Object.assign(Object.assign({}, lesson), { description: lesson.description, duration: undefined, generated: undefined, status: undefined, translations: undefined, uid: undefined });
};
async function startInitialContentGeneration(rigoToken, steps, packageContext, exercise, courseSlug, purposeSlug, lastLesson = "") {
    const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
    console.log("Starting initial content generation for", exSlug);
    const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/initial-content-processor/${exercise.id}/${rigoToken}`;
    // Emit notification that initial content generation is starting
    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
        lesson: exSlug,
        status: "generating",
        log: `🔄 Starting initial content generation for lesson: ${exercise.title}`,
    });
    const fullSyllabus = {
        steps: [steps.map(lessonCleaner)],
        courseInfo: cleanFormStateForSyllabus(packageContext),
        purpose: purposeSlug,
    };
    // Add random 6-digit number to avoid cache issues
    // eslint-disable-next-line no-mixed-operators
    const randomCacheEvict = Math.floor(100000 + Math.random() * 900000);
    const res = await (0, rigoActions_1.initialContentGenerator)(rigoToken.trim(), {
        // prev_lesson: lastLesson,
        output_language: packageContext.language || "en",
        current_syllabus: JSON.stringify(fullSyllabus),
        lesson_description: JSON.stringify(lessonCleaner(exercise)) + `-${randomCacheEvict}`,
    }, webhookUrl);
    console.log("INITIAL CONTENT GENERATOR RES", res);
    return res.id;
}
async function startInteractivityGeneration(rigoToken, steps, packageContext, exercise, courseSlug, purposeSlug, bucket, lastLesson = "") {
    const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
    console.log("Starting interactivity generation for", exSlug);
    const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/interactivity-processor/${exercise.id}/${rigoToken}`;
    // Emit notification that interactivity generation is starting
    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
        lesson: exSlug,
        status: "generating",
        log: `🔄 Starting interactivity generation for lesson: ${exercise.title}`,
    });
    const componentsYml = await fetchComponentsYml();
    // Get current syllabus to include used_components
    const currentSyllabus = await getSyllabus(courseSlug, bucket);
    const fullSyllabus = {
        steps: steps.map(lessonCleaner),
        courseInfo: cleanFormStateForSyllabus(packageContext),
        used_components: currentSyllabus.used_components || {},
    };
    // Add random 6-digit number to avoid cache issues
    // eslint-disable-next-line no-mixed-operators
    const randomCacheEvict = Math.floor(100000 + Math.random() * 900000);
    const res = await (0, rigoActions_1.addInteractivity)(rigoToken.trim(), {
        components: componentsYml,
        prev_lesson: lastLesson,
        initial_lesson: exercise.initialContent + `-${randomCacheEvict}`,
        output_language: packageContext.language || "en",
        current_syllabus: JSON.stringify(fullSyllabus),
    }, webhookUrl);
    console.log("INTERACTIVITY GENERATOR RES", res);
    return res.id;
}
async function createInitialReadme(tutorialInfo, tutorialSlug, rigoToken) {
    const webhookUrl = `${process.env.HOST}/webhooks/${tutorialSlug}/initial-readme-processor`;
    console.log("Creating initial readme", webhookUrl);
    try {
        const res = await (0, rigoActions_1.createStructuredPreviewReadme)(rigoToken, {
            tutorial_info: tutorialInfo,
        }, webhookUrl);
        console.log("Initial readme created", res);
    }
    catch (error) {
        console.error("Error creating initial readme", error);
    }
}
async function getSyllabus(courseSlug, bucket) {
    const syllabus = await bucket.file(`courses/${courseSlug}/.learn/initialSyllabus.json`);
    const [content] = await syllabus.download();
    return JSON.parse(content.toString());
}
async function saveSyllabus(courseSlug, syllabus, bucket) {
    await uploadFileToBucket(bucket, JSON.stringify(syllabus), `courses/${courseSlug}/.learn/initialSyllabus.json`);
}
async function updateUsedComponents(courseSlug, usedComponents, bucket) {
    const syllabus = await getSyllabus(courseSlug, bucket);
    // Initialize used_components if undefined
    if (!syllabus.used_components) {
        syllabus.used_components = {};
    }
    // Update component counts
    for (const componentName of usedComponents) {
        if (syllabus.used_components[componentName]) {
            syllabus.used_components[componentName] += 1;
        }
        else {
            syllabus.used_components[componentName] = 1;
        }
    }
    console.log("Updated component usage:", syllabus.used_components);
    await saveSyllabus(courseSlug, syllabus, bucket);
}
async function updateLessonWithInitialContent(courseSlug, lessonID, initialResponse, bucket) {
    const syllabus = await getSyllabus(courseSlug, bucket);
    const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.id === lessonID);
    if (lessonIndex === -1) {
        console.error(`Lesson ${lessonID} not found in syllabus`);
        return;
    }
    const lesson = syllabus.lessons[lessonIndex];
    // Update initial content
    lesson.initialContent = initialResponse.lesson_content;
    await saveSyllabus(courseSlug, syllabus, bucket);
}
async function updateLessonStatusToError(courseSlug, lessonID, bucket) {
    try {
        const syllabus = await getSyllabus(courseSlug, bucket);
        const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.id === lessonID);
        if (lessonIndex === -1) {
            console.error(`Lesson ${lessonID} not found in syllabus when updating status to error`);
            return;
        }
        const lesson = syllabus.lessons[lessonIndex];
        // Update lesson status to ERROR
        lesson.status = "ERROR";
        // Update translations to mark as failed
        const currentTranslations = lesson.translations || {};
        const language = syllabus.courseInfo.language || "en";
        if (currentTranslations[language]) {
            currentTranslations[language].completedAt = Date.now();
        }
        else {
            currentTranslations[language] = {
                completionId: 0,
                startedAt: Date.now(),
                completedAt: Date.now(),
            };
        }
        lesson.translations = currentTranslations;
        await saveSyllabus(courseSlug, syllabus, bucket);
        console.log(`Updated lesson ${lessonID} status to ERROR in syllabus`);
    }
    catch (error) {
        console.error(`Error updating lesson ${lessonID} status to ERROR:`, error);
    }
}
async function continueWithNextLesson(courseSlug, currentExerciseIndex, rigoToken, finalContent, bucket) {
    const syllabus = await getSyllabus(courseSlug, bucket);
    const nextExercise = syllabus.lessons[currentExerciseIndex + 1] || null;
    let nextCompletionId = null;
    if (nextExercise &&
        (currentExerciseIndex === 0 ||
            !(currentExerciseIndex % 3 === 0) ||
            syllabus.generationMode === "continue-with-all")) {
        let feedback = "";
        if (syllabus.feedback) {
            feedback = `\n\nThe user added the following feedback with relation to the previous generations: ${syllabus.feedback}`;
        }
        nextCompletionId = await startInitialContentGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, nextExercise, courseSlug, syllabus.courseInfo.purpose, finalContent + "\n\n" + feedback);
    }
    else {
        console.log("Stopping generation process at", currentExerciseIndex, syllabus.lessons[currentExerciseIndex].title, "because it's a multiple of 3");
    }
    // Update syllabus with next lesson status
    const newSyllabus = Object.assign(Object.assign({}, syllabus), { lessons: syllabus.lessons.map((lesson, index) => {
            if (index === currentExerciseIndex) {
                const currentTranslations = lesson.translations || {};
                let currentTranslation = currentTranslations[syllabus.courseInfo.language || "en"];
                if (currentTranslation) {
                    currentTranslation.completedAt = Date.now();
                }
                else {
                    currentTranslation = {
                        completionId: nextCompletionId || 0,
                        startedAt: Date.now(),
                        completedAt: Date.now(),
                    };
                }
                currentTranslations[syllabus.courseInfo.language || "en"] =
                    currentTranslation;
                return Object.assign(Object.assign({}, lesson), { generated: true, status: "DONE", translations: {
                        [syllabus.courseInfo.language || "en"]: {
                            completionId: nextCompletionId || 0,
                            startedAt: currentTranslation.startedAt,
                            completedAt: Date.now(),
                        },
                    } });
            }
            if (nextExercise && nextExercise.id === lesson.id && nextCompletionId) {
                return Object.assign(Object.assign({}, lesson), { generated: false, status: "GENERATING", translations: {
                        [syllabus.courseInfo.language || "en"]: {
                            completionId: nextCompletionId,
                            startedAt: Date.now(),
                        },
                    } });
            }
            return Object.assign({}, lesson);
        }) });
    await saveSyllabus(courseSlug, newSyllabus, bucket);
}
const fixPreviewUrl = (slug, previewUrl) => {
    if (!previewUrl) {
        return null;
    }
    if (previewUrl.startsWith("http")) {
        return previewUrl;
    }
    const expectedUrl = `https://${slug}.learn-pack.com/preview.png`;
    console.log("Preview url fixed!", expectedUrl);
    return expectedUrl;
};
const getTitleFromHTML = (html) => {
    const titleRegex = /<title>(.*?)<\/title>/;
    const titleMatch = html.match(titleRegex);
    return titleMatch ? titleMatch[1] : null;
};
class ServeCommand extends SessionCommand_1.default {
    async init() {
        const { flags } = this.parse(ServeCommand);
        console.log("Initializing serve command");
    }
    async run() {
        const crendsEnv = process.env.GCP_CREDENTIALS_JSON;
        if (!crendsEnv) {
            console.log("GCP_CREDENTIALS_JSON is not set");
            process.exit(1);
        }
        await api_1.default.updateTechnologiesPeriodically();
        const credentials = JSON.parse(crendsEnv);
        const bucketStorage = new storage_1.Storage({
            credentials,
        });
        const bucket = bucketStorage.bucket(process.env.GCP_BUCKET_NAME || "learnpack-packages");
        const host = process.env.HOST;
        if (!host) {
            console.log("HOST is not set");
            process.exit(1);
        }
        else {
            console.log("HOST is set to", host);
        }
        // async function listFilesWithPrefix(prefix: string) {
        //   const [files] = await bucket.getFiles({ prefix })
        //   return files
        // }
        const app = express();
        const server = http.createServer(app);
        (0, creatorSocket_1.initSocketIO)(server);
        const PORT = process.env.PORT || 3000;
        const distPath = path.resolve(__dirname, "../creatorDist");
        // Servir archivos estáticos
        // app.use(express.static(distPath))
        app.use(express.json({ limit: "20mb" }));
        app.use(cors());
        const appPath = path.resolve(__dirname, "../ui/_app");
        const tarPath = path.resolve(__dirname, "../ui/app.tar.gz");
        if (fs.existsSync(appPath)) {
            fs.rmSync(appPath, { recursive: true });
        }
        if (fs.existsSync(tarPath)) {
            fs.rmSync(tarPath);
        }
        await (0, file_1.downloadEditor)("5.0.0", `${__dirname}/../ui/app.tar.gz`);
        await (0, file_1.decompress)(`${__dirname}/../ui/app.tar.gz`, `${__dirname}/../ui/_app/`);
        const localAppPath = path.resolve(__dirname, "../ui/_app");
        // app.use(express.static(localAppPath))
        app.post("/upload", async (req, res) => {
            const { content, destination } = req.body;
            //   console.log("UPLOAD", content, destination)
            if (!content || !destination) {
                return res.status(400).send("Missing content or destination");
            }
            if (!bucket) {
                return res.status(500).send("Upload failed");
            }
            const buffer = buffer_1.Buffer.from(content, "utf-8");
            const file = bucket.file(destination);
            const stream = file.createWriteStream({
                resumable: false,
                contentType: "text/plain",
            });
            stream.on("error", err => {
                console.error("❌ Error uploading:", err);
                res.status(500).send("Upload failed");
            });
            stream.on("finish", () => {
                console.log(`✅ Uploaded to: ${file.name}`);
                res.send("File uploaded successfully");
            });
            stream.end(buffer);
        });
        const upload = (0, misc_1.createUploadMiddleware)();
        app.post("/upload-image-file", upload.single("file"), async (req, res) => {
            console.log("UPLOADING IMAGE FILE");
            const destination = req.body.destination;
            // eslint-disable-next-line
            // @ts-ignore
            if (!req.file || !destination) {
                return res
                    .status(400)
                    .json({ error: "File and destination are required" });
            }
            try {
                // eslint-disable-next-line
                // @ts-ignore
                const fileData = req.file.buffer;
                const file = bucket.file(destination);
                await file.save(fileData, {
                    resumable: false,
                    // eslint-disable-next-line
                    // @ts-ignore
                    contentType: req.file.mimetype,
                });
                console.log(`✅ Image uploaded to ${file.name}`);
                res.json({ message: "Image uploaded successfully", path: file.name });
            }
            catch (error) {
                console.error("❌ upload-image-file error:", error);
                res.status(500).json({ error: error.message });
            }
        });
        app.put("/upload/preview-image/:slug", (0, misc_1.createUploadMiddleware)().single("file"), async (req, res) => {
            const { slug } = req.params;
            const rigoToken = req.header("x-rigo-token");
            if (!rigoToken) {
                return res.status(400).json({
                    error: "Rigo token is required. x-rigo-token header is missing",
                });
            }
            const { isAuthor } = await (0, rigoActions_1.isPackageAuthor)(rigoToken, slug);
            if (!isAuthor) {
                return res.status(403).json({
                    error: "You are not authorized to upload a preview image",
                });
            }
            // eslint-disable-next-line
            // @ts-ignore
            if (!slug || !req.file) {
                return res.status(400).json({ error: "Slug and file are required" });
            }
            try {
                const filePath = `courses/${slug}/preview.png`;
                const file = bucket.file(filePath);
                // eslint-disable-next-line
                // @ts-ignore
                await file.save(req.file.buffer, {
                    resumable: false,
                    // eslint-disable-next-line
                    // @ts-ignore
                    contentType: req.file.mimetype,
                });
                console.log(`✅ Preview image uploaded to ${filePath}`);
                return res.json({ message: "Preview uploaded", path: filePath });
            }
            catch (error) {
                console.error("❌ Error uploading preview image:", error);
                return res.status(500).json({ error: error.message });
            }
        });
        app.post("/notifications/:id", async (req, res) => {
            console.log("Receiving a webhook to notification id", req.params.id);
            const { id } = req.params;
            const body = req.body;
            (0, creatorSocket_1.emitToNotification)(id, body);
            res.json({ id, status: "SUCCESS" });
        });
        app.post("/webhooks/:courseSlug/initial-readme-processor", async (req, res) => {
            const { courseSlug } = req.params;
            const body = req.body;
            console.log("RECEIVING INITIAL README WEBHOOK", body);
            const langCode = body.parsed.language_code || body.parsed.output_language_code || "";
            const content = body.parsed.content || body.parsed.translation || "";
            if (!content || !langCode) {
                console.log("No content or language code to save", body);
                return res.status(400).json({
                    status: "ERROR",
                    message: "No content to save",
                });
            }
            const filePath = `courses/${courseSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(langCode)}`;
            await uploadFileToBucket(bucket, content, filePath);
            res.json({ status: "SUCCESS" });
        });
        app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
            const { courseSlug, imageId } = req.params;
            const body = req.body;
            try {
                if (body.error) {
                    (0, creatorSocket_1.emitToNotification)(imageId, {
                        status: "ERROR",
                        message: "Error generating image",
                    });
                    return res.json({ status: "ERROR" });
                }
                const imageUrl = body.image_url;
                const format = body.format;
                const imagePath = `courses/${courseSlug}/.learn/assets/${imageId}`;
                const imageFile = bucket.file(imagePath);
                const [exists] = await imageFile.exists();
                if (!exists) {
                    let buffer;
                    if (format === "b64" || imageUrl.startsWith("data:image/")) {
                        const base64Data = imageUrl.includes(",") ?
                            imageUrl.split(",")[1] :
                            imageUrl;
                        buffer = buffer_1.Buffer.from(base64Data, "base64");
                    }
                    else if (imageUrl.startsWith("http")) {
                        const response = await fetch(imageUrl);
                        if (!response.ok) {
                            return res.status(400).json({
                                status: "ERROR",
                                message: "Could not download the image",
                            });
                        }
                        buffer = new Uint8Array(await response.arrayBuffer());
                    }
                    else {
                        return res.status(400).json({
                            status: "ERROR",
                            message: "Image_url format not supported",
                        });
                    }
                    await imageFile.save(buffer, { contentType: "image/png" });
                }
                (0, creatorSocket_1.emitToNotification)(imageId, {
                    status: "SUCCESS",
                    message: "Image generated successfully",
                });
                res.json({ status: "SUCCESS" });
            }
            catch (error) {
                (0, creatorSocket_1.emitToNotification)(imageId, {
                    status: "ERROR",
                    message: "Error receiving image webhook",
                });
                console.error("❌ Error receiving image webhook:", error);
                res.status(500).json({ error: error.message });
            }
        });
        app.post("/actions/continue-generating/:courseSlug/:position", async (req, res) => {
            const { courseSlug, position } = req.params;
            const { feedback, mode } = req.body;
            const rigoToken = req.header("x-rigo-token");
            if (!rigoToken) {
                return res.status(400).json({
                    error: "Rigo token is required. x-rigo-token header is missing",
                });
            }
            const syllabus = await getSyllabus(courseSlug, bucket);
            const exercise = syllabus.lessons[parseInt(position)];
            // previous exercise
            let previousReadme = "---";
            const previousExercise = syllabus.lessons[parseInt(position) - 1];
            if (previousExercise) {
                // Get the readme of the previous exercise
                const exSlug = (0, creatorUtilities_2.slugify)(previousExercise.id + "-" + previousExercise.title);
                // llist the files un
                const [files] = await bucket.getFiles({
                    prefix: `courses/${courseSlug}/exercises/${exSlug}/README`,
                });
                // select any README
                const readmeFiles = files.map(f => f.name);
                const readmeFile = readmeFiles.find(f => f.includes("README"));
                if (readmeFile) {
                    const [content] = await bucket.file(readmeFile).download();
                    previousReadme = content.toString();
                }
            }
            // Use new two-phase generation workflow
            const completionId = await startInitialContentGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, exercise, courseSlug, syllabus.courseInfo.purpose, previousReadme +
                "\n\nThe user provided the following feedback related to the content of the course so far: \n\n" +
                feedback);
            syllabus.lessons[parseInt(position)].status = "GENERATING";
            syllabus.lessons[parseInt(position)].translations = {
                [syllabus.courseInfo.language || "en"]: {
                    completionId,
                    startedAt: Date.now(),
                    completedAt: 0,
                },
            };
            console.log("Lesson", syllabus.lessons[parseInt(position)]);
            if (syllabus.feedback && typeof syllabus.feedback === "string") {
                syllabus.feedback += "\n\n" + feedback;
            }
            else {
                syllabus.feedback = feedback;
            }
            syllabus.generationMode = mode;
            await saveSyllabus(courseSlug, syllabus, bucket);
            res.json({ status: "SUCCESS" });
        });
        // TODO: Check if this command is being used
        app.post("/actions/generate-image/:courseSlug", async (req, res) => {
            const rigoToken = req.header("x-rigo-token");
            const { courseSlug } = req.params;
            const { image } = req.body;
            if (!image) {
                return res.status(400).json({
                    error: "Image is required",
                });
            }
            if (!rigoToken) {
                return res.status(400).json({
                    error: "Rigo token is required. x-rigo-token header is missing",
                });
            }
            await (0, exports.processImage)(image.url, image.alt, rigoToken, courseSlug);
            res.json({ status: "QUEUED" });
        });
        app.post("/webhooks/:courseSlug/exercise-processor/:lessonID/:rigoToken", async (req, res) => {
            // console.log("Receiving a webhook to exercise processor")
            const { courseSlug, lessonID, rigoToken } = req.params;
            const readme = req.body;
            const syllabus = await bucket.file(`courses/${courseSlug}/.learn/initialSyllabus.json`);
            const [content] = await syllabus.download();
            const syllabusJson = JSON.parse(content.toString());
            if (readme.status === "ERROR") {
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: lessonID,
                    status: "error",
                    log: `❌ Error generating the lesson ${lessonID}`,
                });
            }
            const exerciseIndex = syllabusJson.lessons.findIndex(lesson => lesson.id === lessonID);
            if (exerciseIndex === -1) {
                console.log("Exercise not found receiving webhook, this should not happen", lessonID);
                return res.json({ status: "ERROR", error: "Exercise not found" });
            }
            const exercise = syllabusJson.lessons[exerciseIndex];
            if (!exercise) {
                return res.json({
                    status: "ERROR",
                    error: "Exercise not found or is invalid",
                });
            }
            const nextExercise = syllabusJson.lessons[exerciseIndex + 1] || null;
            const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
            const readability = (0, creatorUtilities_2.checkReadability)(readme.parsed.content, PARAMS.max_words, 3);
            (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                lesson: exSlug,
                status: "generating",
                log: `🔄 The lesson ${exercise.title} has a readability score of ${readability.fkglResult.fkgl}`,
            });
            const exercisesDir = `courses/${courseSlug}/exercises`;
            const targetDir = `${exercisesDir}/${exSlug}`;
            const readmeFilename = `README${(0, creatorUtilities_1.getReadmeExtension)(readme.parsed.language_code)}`;
            await uploadFileToBucket(bucket, readability.newMarkdown, `${targetDir}/${readmeFilename}`);
            if (exercise.type.toLowerCase() === "code" &&
                readme.parsed.codefile_content) {
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: exSlug,
                    status: "generating",
                    log: `🔄 Creating code file for ${exercise.title}`,
                });
                await uploadFileToBucket(bucket, readme.parsed.codefile_content, `${targetDir}/${readme.parsed.codefile_name.toLowerCase().trim()}`);
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: exSlug,
                    status: "generating",
                    log: `✅ Code file created for ${exercise.title}`,
                });
                if (readme.parsed.solution_content) {
                    const codeFileName = readme.parsed.codefile_name
                        .toLowerCase()
                        .trim();
                    const solutionFileName = "solution.hide." + codeFileName;
                    await uploadFileToBucket(bucket, readme.parsed.solution_content, `${targetDir}/${solutionFileName}`);
                    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                        lesson: exSlug,
                        status: "generating",
                        log: `✅ Solution file created for ${exercise.title}`,
                    });
                }
            }
            let nextCompletionId = null;
            if (nextExercise &&
                (exerciseIndex === 0 ||
                    !(exerciseIndex % 3 === 0) ||
                    syllabusJson.generationMode === "continue-with-all")) {
                let feedback = "";
                if (syllabusJson.feedback) {
                    feedback = `\n\nThe user added the following feedback with relation to the previous generations: ${syllabusJson.feedback}`;
                }
                nextCompletionId = await startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, nextExercise, courseSlug, syllabusJson.courseInfo.purpose, readme.parsed.content + "\n\n" + feedback);
            }
            else {
                console.log("Stopping generation process at", exerciseIndex, exercise.title, "because it's a multiple of 3");
            }
            const newSyllabus = Object.assign(Object.assign({}, syllabusJson), { lessons: syllabusJson.lessons.map((lesson, index) => {
                    if (index === exerciseIndex) {
                        const currentTranslations = lesson.translations || {};
                        let currentTranslation = currentTranslations[syllabusJson.courseInfo.language || "en"];
                        if (currentTranslation) {
                            currentTranslation.completedAt = Date.now();
                        }
                        else {
                            currentTranslation = {
                                completionId: readme.id,
                                startedAt: Date.now(),
                                completedAt: Date.now(),
                            };
                        }
                        currentTranslations[syllabusJson.courseInfo.language || "en"] =
                            currentTranslation;
                        return Object.assign(Object.assign({}, lesson), { generated: true, status: "DONE", translations: {
                                [syllabusJson.courseInfo.language || "en"]: {
                                    completionId: nextCompletionId,
                                    completedAt: Date.now(),
                                },
                            } });
                    }
                    if (nextExercise &&
                        nextExercise.id === lesson.id &&
                        nextCompletionId) {
                        return Object.assign(Object.assign({}, lesson), { generated: false, status: "GENERATING", translations: {
                                [syllabusJson.courseInfo.language || "en"]: {
                                    completionId: nextCompletionId,
                                    startedAt: Date.now(),
                                },
                            } });
                    }
                    return Object.assign({}, lesson);
                }) });
            console.log("New syllabus", newSyllabus);
            await uploadFileToBucket(bucket, JSON.stringify(newSyllabus), `courses/${courseSlug}/.learn/initialSyllabus.json`);
            (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                lesson: exSlug,
                status: "done",
                log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
            });
            res.json({ status: "SUCCESS" });
        });
        // Phase 1: Initial content generation webhook
        app.post("/webhooks/:courseSlug/initial-content-processor/:lessonID/:rigoToken", async (req, res) => {
            const { courseSlug, lessonID, rigoToken } = req.params;
            const response = req.body;
            console.log("RECEIVING INITIAL CONTENT WEBHOOK", response);
            // Handle errors
            if (response.status === "ERROR") {
                await updateLessonStatusToError(courseSlug, lessonID, bucket);
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: lessonID,
                    status: "error",
                    log: `❌ Error generating initial content for lesson ${lessonID}`,
                });
                // Retry initial content generation
                try {
                    const syllabus = await getSyllabus(courseSlug, bucket);
                    const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.id === lessonID);
                    const exercise = syllabus.lessons[lessonIndex];
                    if (exercise) {
                        (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                            lesson: lessonID,
                            status: "generating",
                            log: `🔄 Retrying initial content generation for lesson ${lessonID}`,
                        });
                        const retryCompletionId = await startInitialContentGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, exercise, courseSlug, syllabus.courseInfo.purpose, "");
                        // Update lesson status to show it's retrying
                        exercise.status = "GENERATING";
                        exercise.translations = {
                            [syllabus.courseInfo.language || "en"]: {
                                completionId: retryCompletionId,
                                startedAt: Date.now(),
                                completedAt: 0,
                            },
                        };
                        await saveSyllabus(courseSlug, syllabus, bucket);
                    }
                }
                catch (retryError) {
                    console.error("Error retrying initial content generation:", retryError);
                    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                        lesson: lessonID,
                        status: "error",
                        log: `❌ Failed to retry initial content generation for lesson ${lessonID}`,
                    });
                }
                return res.json({ status: "ERROR" });
            }
            try {
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: lessonID,
                    status: "generating",
                    log: `✅ Initial content generated for lesson ${lessonID}`,
                });
                // Update lesson with initial content
                await updateLessonWithInitialContent(courseSlug, lessonID, response.parsed, bucket);
                // Start Phase 2: Add interactivity
                const syllabus = await getSyllabus(courseSlug, bucket);
                const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.id === lessonID);
                const exercise = syllabus.lessons[lessonIndex];
                let lastLesson = "";
                const prevLessonIndex = lessonIndex - 1;
                if (prevLessonIndex >= 0) {
                    try {
                        const prevLesson = syllabus.lessons[prevLessonIndex];
                        // search the lesson content in the bucket
                        const prevLessonSlug = (0, creatorUtilities_2.slugify)(prevLesson.id + "-" + prevLesson.title);
                        const file = bucket.file(`courses/${courseSlug}/exercises/${prevLessonSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(syllabus.courseInfo.language || "en")}`);
                        const [content] = await file.download();
                        lastLesson = content.toString();
                    }
                    catch (error) {
                        console.error("Error searching previous lesson content:", error);
                    }
                }
                if (exercise) {
                    const completionId = await startInteractivityGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, exercise, courseSlug, syllabus.courseInfo.purpose, bucket, lastLesson);
                    // Update lesson status to show it's in Phase 2
                    exercise.status = "GENERATING";
                    exercise.translations = {
                        [syllabus.courseInfo.language || "en"]: {
                            completionId,
                            startedAt: Date.now(),
                            completedAt: 0,
                        },
                    };
                    await saveSyllabus(courseSlug, syllabus, bucket);
                    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                        lesson: lessonID,
                        status: "generating",
                        log: `🔄 Starting interactivity phase for lesson ${exercise.title}`,
                    });
                }
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: lessonID,
                    status: "initial-content-complete",
                    log: `✅ Initial content generated for lesson ${lessonID}, starting interactivity phase`,
                });
                res.json({ status: "SUCCESS" });
            }
            catch (error) {
                console.error("Error processing initial content webhook:", error);
                await updateLessonStatusToError(courseSlug, lessonID, bucket);
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: lessonID,
                    status: "error",
                    log: `❌ Error processing initial content for lesson ${lessonID}`,
                });
                res
                    .status(500)
                    .json({ status: "ERROR", error: error.message });
            }
        });
        // Phase 2: Interactivity generation webhook (replaces exercise-processor logic)
        app.post("/webhooks/:courseSlug/interactivity-processor/:lessonID/:rigoToken", async (req, res) => {
            const { courseSlug, lessonID, rigoToken } = req.params;
            const response = req.body;
            console.log("RECEIVING INTERACTIVITY WEBHOOK", response);
            // Handle errors
            if (response.status === "ERROR") {
                await updateLessonStatusToError(courseSlug, lessonID, bucket);
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: lessonID,
                    status: "error",
                    log: `❌ Error adding interactivity to lesson ${lessonID}`,
                });
                // Retry interactivity generation
                try {
                    const syllabus = await getSyllabus(courseSlug, bucket);
                    const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.id === lessonID);
                    const exercise = syllabus.lessons[lessonIndex];
                    if (exercise && exercise.initialContent) {
                        (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                            lesson: lessonID,
                            status: "generating",
                            log: `🔄 Retrying interactivity generation for lesson ${lessonID}`,
                        });
                        // Get previous lesson content for context
                        let lastLesson = "";
                        const prevLessonIndex = lessonIndex - 1;
                        if (prevLessonIndex >= 0) {
                            try {
                                const prevLesson = syllabus.lessons[prevLessonIndex];
                                const prevLessonSlug = (0, creatorUtilities_2.slugify)(prevLesson.id + "-" + prevLesson.title);
                                const file = bucket.file(`courses/${courseSlug}/exercises/${prevLessonSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(syllabus.courseInfo.language || "en")}`);
                                const [content] = await file.download();
                                lastLesson = content.toString();
                            }
                            catch (error) {
                                console.error("Error getting previous lesson content for retry:", error);
                            }
                        }
                        const retryCompletionId = await startInteractivityGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, exercise, courseSlug, syllabus.courseInfo.purpose, bucket, lastLesson);
                        // Update lesson status to show it's retrying
                        exercise.status = "GENERATING";
                        exercise.translations = {
                            [syllabus.courseInfo.language || "en"]: {
                                completionId: retryCompletionId,
                                startedAt: Date.now(),
                                completedAt: 0,
                            },
                        };
                        await saveSyllabus(courseSlug, syllabus, bucket);
                    }
                }
                catch (retryError) {
                    console.error("Error retrying interactivity generation:", retryError);
                    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                        lesson: lessonID,
                        status: "error",
                        log: `❌ Failed to retry interactivity generation for lesson ${lessonID}`,
                    });
                }
                return res.json({ status: "ERROR" });
            }
            try {
                const syllabus = await getSyllabus(courseSlug, bucket);
                const exerciseIndex = syllabus.lessons.findIndex(lesson => lesson.id === lessonID);
                if (exerciseIndex === -1) {
                    console.error("Exercise not found receiving webhook:", lessonID);
                    return res.json({ status: "ERROR", error: "Exercise not found" });
                }
                const exercise = syllabus.lessons[exerciseIndex];
                const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
                // Process readability of final content
                const readability = (0, creatorUtilities_2.checkReadability)(response.parsed.final_content, PARAMS.max_words, 3);
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: exSlug,
                    status: "generating",
                    log: `🔄 The lesson ${exercise.title} has a readability score of ${readability.fkglResult.fkgl}`,
                });
                // Upload README with final content
                const exercisesDir = `courses/${courseSlug}/exercises`;
                const targetDir = `${exercisesDir}/${exSlug}`;
                const readmeFilename = `README${(0, creatorUtilities_1.getReadmeExtension)(response.parsed.output_language ||
                    syllabus.courseInfo.language ||
                    "en")}`;
                await uploadFileToBucket(bucket, readability.newMarkdown, `${targetDir}/${readmeFilename}`);
                // Handle code files if it's a coding exercise
                if (exercise.type.toLowerCase() === "code" &&
                    response.parsed.codefile_content) {
                    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                        lesson: exSlug,
                        status: "generating",
                        log: `🔄 Creating code file for ${exercise.title}`,
                    });
                    await uploadFileToBucket(bucket, response.parsed.codefile_content, `${targetDir}/${response.parsed.codefile_name
                        .toLowerCase()
                        .trim()}`);
                    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                        lesson: exSlug,
                        status: "generating",
                        log: `✅ Code file created for ${exercise.title}`,
                    });
                    if (response.parsed.solution_content) {
                        const codeFileName = response.parsed.codefile_name
                            .toLowerCase()
                            .trim();
                        const solutionFileName = "solution.hide." + codeFileName;
                        await uploadFileToBucket(bucket, response.parsed.solution_content, `${targetDir}/${solutionFileName}`);
                        (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                            lesson: exSlug,
                            status: "generating",
                            log: `✅ Solution file created for ${exercise.title}`,
                        });
                    }
                }
                // Update used components if provided by the AI
                if (response.parsed.used_components &&
                    Array.isArray(response.parsed.used_components)) {
                    await updateUsedComponents(courseSlug, response.parsed.used_components, bucket);
                    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                        lesson: exSlug,
                        status: "generating",
                        log: `📊 Updated component usage tracking: ${response.parsed.used_components.join(", ")}`,
                    });
                }
                // Continue with next lesson
                await continueWithNextLesson(courseSlug, exerciseIndex, rigoToken, response.parsed.final_content, bucket);
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: exSlug,
                    status: "done",
                    log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully with interactivity!`,
                });
                res.json({ status: "SUCCESS" });
            }
            catch (error) {
                console.error("Error processing interactivity webhook:", error);
                await updateLessonStatusToError(courseSlug, lessonID, bucket);
                (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
                    lesson: lessonID,
                    status: "error",
                    log: `❌ Error processing interactivity for lesson ${lessonID}`,
                });
                res
                    .status(500)
                    .json({ status: "ERROR", error: error.message });
            }
        });
        // The following endpoint is used to store an incoming translation where it supposed to be
        app.post("/webhooks/:courseSlug/:exSlug/save-translation", async (req, res) => {
            const { courseSlug, exSlug } = req.params;
            const body = req.body;
            console.log("RECEIVING TRANSLATION WEBHOOK", body);
            const readmePath = `courses/${courseSlug}/exercises/${exSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(body.parsed.output_language_code)}`;
            await uploadFileToBucket(bucket, body.parsed.translation, readmePath);
            res.json({ status: "SUCCESS" });
        });
        app.get("/check-preview-image/:slug", async (req, res) => {
            const { slug } = req.params;
            const file = bucket.file(`courses/${slug}/preview.png`);
            const [exists] = await file.exists();
            res.json({ exists, file: file.name });
        });
        app.post("/webhooks/:courseSlug/:exSlug/:lang/update-readme/:notificationId", async (req, res) => {
            console.log("RECEIVING README UPDATE WEBHOOK");
            const { courseSlug, exSlug, lang, notificationId } = req.params;
            const body = req.body;
            console.log("BODY", body);
            const readmePath = `courses/${courseSlug}/exercises/${exSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(lang)}`;
            if (body.parsed.content) {
                await uploadFileToBucket(bucket, body.parsed.content, readmePath);
            }
            (0, creatorSocket_1.emitToNotification)(notificationId, {
                status: "SUCCESS",
                message: "Readme updated successfully",
            });
            res.json({ status: "SUCCESS" });
        });
        app.get("/create", (req, res) => {
            console.log("GET /create");
            res.redirect("/creator/");
        });
        app.get("/", async (req, res) => {
            console.log("GET /");
            const slug = req.query.slug;
            if (slug) {
                const file = path.resolve(__dirname, "../ui/_app/index.html");
                res.sendFile(file);
            }
            else {
                res.redirect("https://learnpack.co");
            }
        });
        app.get("/preview/:slug", async (req, res) => {
            console.log("GET COURSE PREVIEW /");
            const file = path.resolve(__dirname, "../ui/_app/index.html");
            res.sendFile(file);
        });
        app.get("/config", async (req, res) => {
            const courseSlug = req.query.slug;
            // GEt the x-rigo-token
            // const rigoToken = req.header("x-rigo-token")
            if (!courseSlug) {
                return res
                    .status(400)
                    .json({ error: "Course slug and rigo token required" });
            }
            try {
                const { config, exercises } = await (0, configBuilder_1.buildConfig)(bucket, courseSlug);
                console.log("CONFIG", config);
                console.log("EXERCISES", exercises);
                res.set("X-Creator-Web", "true");
                res.set("Access-Control-Expose-Headers", "X-Creator-Web");
                await uploadFileToBucket(bucket, JSON.stringify({ config, exercises }), `courses/${courseSlug}/.learn/config.json`);
                res.json({ config, exercises });
            }
            catch (error) {
                console.error(error);
                res.status(500).json({ error: error.message });
            }
        });
        app.get("/exercise/:slug/readme", async (req, res) => {
            console.log("GET /exercise/:slug/readme");
            const { slug } = req.params;
            const courseSlug = req.query.slug;
            const lang = req.query.lang || "en";
            if (!courseSlug) {
                return res.status(400).json({ error: "Missing courseSlug" });
            }
            const basePath = `courses/${courseSlug}/exercises/${slug}/`;
            const prefix = basePath + "README";
            try {
                const [files] = await bucket.getFiles({ prefix });
                const readmeFiles = files.map(f => f.name);
                if (readmeFiles.length === 0) {
                    return res.status(404).json({ error: "No README files found" });
                }
                const requestedFilename = `${basePath}README${(0, creatorUtilities_1.getReadmeExtension)(lang)}`;
                let selectedFile = readmeFiles.find(f => f === requestedFilename);
                if (!selectedFile) {
                    console.log("No se encontro en ese idioma, devolviendo el primero");
                    selectedFile = readmeFiles[0];
                    console.warn(`Requested README not found for lang '${lang}', using '${selectedFile}'`);
                }
                let foundLang = "en";
                const match = selectedFile.match(/readme(?:\.([a-z]{2}))?\.md$/i);
                if (match && match[1]) {
                    foundLang = match[1].toLowerCase();
                }
                const [contentBuffer] = await bucket.file(selectedFile).download();
                try {
                    const { attributes, body } = frontMatter(contentBuffer.toString());
                    res.send({ attributes, body, lang: foundLang });
                }
                catch (_a) {
                    res.status(200).json({
                        attributes: {},
                        body: contentBuffer.toString(),
                        lang: foundLang,
                    });
                }
            }
            catch (error) {
                console.error(error);
                res.status(500).json({ error: "Internal server error" });
            }
        });
        app.get("/.learn/assets/:file", async (req, res) => {
            console.log("GET /.learn/assets/:file", req.params.file);
            const { file } = req.params;
            const courseSlug = req.query.slug;
            if (!courseSlug) {
                return res.status(400).send("Course slug is required");
            }
            const filePath = `courses/${courseSlug}/.learn/assets/${file}`;
            const fileRef = bucket.file(filePath);
            const [exists] = await fileRef.exists();
            if (!exists) {
                return res.status(404).send("File not found");
            }
            const fileStream = fileRef.createReadStream();
            res.set("Content-Type", "application/octet-stream");
            res.set("Content-Disposition", `attachment; filename="${file}"`);
            fileStream.pipe(res);
        });
        app.put("/exercise/:slug/file/:fileName", express.text(), async (req, res) => {
            const { slug, fileName } = req.params;
            const query = req.query;
            console.log(`PUT /exercise/${slug}/file/${fileName}`);
            const courseSlug = query.slug;
            // console.log("COURSE SLUG", courseSlug)
            // Update the file in the bucket
            const file = await bucket.file(`courses/${courseSlug}/exercises/${slug}/${fileName}`);
            await file.save(req.body);
            const created = await file.exists();
            // console.log("File updated", created)
            res.send({
                message: "File updated",
                created,
            });
        });
        app.post("/exercise/:slug/create", async (req, res) => {
            console.log("POST /exercise/:slug/create");
            const query = req.query;
            const { title, readme, language } = req.body;
            if (!title || !readme) {
                return res
                    .status(400)
                    .json({ error: "Missing title or readme content" });
            }
            const courseSlug = query.slug;
            const fileName = `courses/${courseSlug}/exercises/${title}/README${(0, creatorUtilities_1.getReadmeExtension)(language)}`;
            const file = bucket.file(fileName);
            await file.save(readme);
            const created = await file.exists();
            res.send({
                message: "File updated",
                created,
            });
        });
        app.put("/actions/rename", async (req, res) => {
            console.log("PUT /actions/rename");
            const { slug, newSlug } = req.body;
            const query = req.query;
            const courseSlug = query.slug;
            const filePrefix = `courses/${courseSlug}/exercises/${slug}/`;
            const [files] = await bucket.getFiles({ prefix: filePrefix });
            for (const file of files) {
                const newFileName = file.name.replace(slug, newSlug);
                // eslint-disable-next-line no-await-in-loop
                await file.rename(newFileName);
            }
            res.send({ message: "Files renamed" });
        });
        app.post("/actions/translate", express.json(), async (req, res) => {
            console.log("POST /actions/translate");
            const { exerciseSlugs, languages, rigoToken, currentLanguage } = req.body;
            const query = req.query;
            const courseSlug = query.slug;
            if (!rigoToken) {
                return res.status(400).json({ error: "RigoToken not found" });
            }
            const languagesToTranslate = languages.split(",");
            const languageCodesRes = await (0, rigoActions_1.getLanguageCodes)(rigoToken, {
                raw_languages: languagesToTranslate.join(","),
            });
            const languageCodes = languageCodesRes.parsed.language_codes;
            try {
                await Promise.all(exerciseSlugs.map(async (slug) => {
                    const readmePath = `courses/${courseSlug}/exercises/${slug}/README${(0, creatorUtilities_1.getReadmeExtension)(currentLanguage)}`;
                    const readme = await bucket.file(readmePath).download();
                    await Promise.all(languageCodes.map(async (language) => {
                        // verify if the translation already exists
                        const translationPath = `courses/${courseSlug}/exercises/${slug}/README${(0, creatorUtilities_1.getReadmeExtension)(language)}`;
                        const [exists] = await bucket.file(translationPath).exists();
                        if (exists) {
                            console.log(`Translation in ${language} already exists for exercise ${slug}`);
                            return;
                        }
                        await (0, rigoActions_1.translateExercise)(rigoToken, {
                            text_to_translate: readme.toString(),
                            output_language: language,
                        }, `${process.env.HOST}/webhooks/${courseSlug}/${slug}/save-translation`);
                    }));
                }));
                const course = await bucket
                    .file(`courses/${courseSlug}/learn.json`)
                    .download();
                const courseJson = JSON.parse(course.toString());
                const neededLanguages = new Set();
                let currentLanguages = Object.keys(courseJson.title);
                for (const languageCode of languageCodes) {
                    if (!currentLanguages.includes(languageCode)) {
                        neededLanguages.add(languageCode);
                    }
                }
                const neededLanguagesList = [...neededLanguages]
                    .map((lang) => lang.toLowerCase())
                    .sort();
                if (neededLanguagesList.length > 0) {
                    const result = await (0, rigoActions_1.translateCourseMetadata)(rigoToken, {
                        title: JSON.stringify(courseJson.title),
                        description: JSON.stringify(courseJson.description),
                        new_languages: neededLanguagesList.join(","),
                    });
                    const translateTitle = JSON.parse(result.parsed.title);
                    const translateDescription = JSON.parse(result.parsed.description);
                    courseJson.title = translateTitle;
                    courseJson.description = translateDescription;
                    await uploadFileToBucket(bucket, JSON.stringify(courseJson), `courses/${courseSlug}/learn.json`);
                    currentLanguages = Object.keys(courseJson.title);
                }
                // Check that all the READMEs exists
                const missingReadmeTranslations = [];
                let firstAvailable = "";
                for (const languageCode of currentLanguages) {
                    // eslint-disable-next-line no-await-in-loop
                    const previewReadme = await bucket.file(`courses/${courseSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(languageCode)}`);
                    // eslint-disable-next-line no-await-in-loop
                    const [exists] = await previewReadme.exists();
                    if (!exists) {
                        missingReadmeTranslations.push(languageCode);
                    }
                    else {
                        // eslint-disable-next-line no-await-in-loop
                        const [previewReadmeContent] = await previewReadme.download();
                        const previewReadmeContentString = previewReadmeContent.toString();
                        firstAvailable = previewReadmeContentString;
                    }
                }
                await Promise.all(missingReadmeTranslations.map(async (languageCode) => {
                    await (0, rigoActions_1.translateExercise)(rigoToken, {
                        text_to_translate: firstAvailable,
                        output_language: languageCode,
                    }, `${process.env.HOST}/webhooks/${courseSlug}/initial-readme-processor`);
                }));
                return res.status(200).json({ message: "Translated exercises" });
            }
            catch (error) {
                console.log(error, "ERROR");
                return res.status(400).json({ error: error.message });
            }
        });
        app.delete("/exercise/:slug/delete", async (req, res) => {
            console.log("DELETE /exercise/:slug/delete");
            const { slug } = req.params;
            const query = req.query;
            const courseSlug = query.slug;
            const filePrefix = `courses/${courseSlug}/exercises/${slug}/`;
            const [files] = await bucket.getFiles({ prefix: filePrefix });
            for (const file of files) {
                // eslint-disable-next-line no-await-in-loop
                await file.delete();
            }
            res.send({ message: "Files deleted" });
        });
        app.get("/translations/sidebar", async (req, res) => {
            const { slug } = req.query;
            const rigoToken = req.header("x-rigo-token");
            console.log("GETTING SIDEBAR FOR", slug);
            if (!slug) {
                return res.status(400).json({ error: "Slug is required" });
            }
            const courseSlug = slug;
            try {
                const file = bucket.file(`courses/${courseSlug}/.learn/sidebar.json`);
                const [content] = await file.download();
                const sidebar = JSON.parse(content.toString());
                if (rigoToken) {
                    const { exercises } = await (0, configBuilder_1.buildConfig)(bucket, courseSlug);
                    const { fixedSidebar, valid } = await (0, sidebarGenerator_1.checkAndFixSidebarPure)(sidebar, exercises, rigoToken);
                    if (fixedSidebar) {
                        await uploadFileToBucket(bucket, JSON.stringify(fixedSidebar), `courses/${courseSlug}/.learn/sidebar.json`);
                        return res.json(fixedSidebar);
                    }
                }
                res.json(sidebar);
            }
            catch (error) {
                if (error.code === 404) {
                    const { exercises } = await (0, configBuilder_1.buildConfig)(bucket, courseSlug);
                    const exerciseSlugsArray = exercises.map(exercise => exercise.slug);
                    const sidebar = await createInitialSidebar(exerciseSlugsArray);
                    await uploadFileToBucket(bucket, JSON.stringify(sidebar), `courses/${courseSlug}/.learn/sidebar.json`);
                    if (rigoToken) {
                        const { fixedSidebar } = await (0, sidebarGenerator_1.checkAndFixSidebarPure)(sidebar, exercises, rigoToken);
                        if (fixedSidebar) {
                            await uploadFileToBucket(bucket, JSON.stringify(fixedSidebar), `courses/${courseSlug}/.learn/sidebar.json`);
                        }
                        return res.status(200).json(fixedSidebar);
                    }
                    return res.status(200).json(sidebar);
                }
                console.error("Unexpected error:", error);
                res.status(500).json({ error: error.message });
            }
        });
        app.get("/technologies", async (req, res) => {
            console.log("GET /technologies");
            const technologies = await api_1.default.getCurrentTechnologies();
            res.json(technologies);
        });
        app.get("/assets/:file", (req, res) => {
            const file = path.join(localAppPath, req.params.file);
            res.sendFile(file);
        });
        app.get("/creator", (req, res) => {
            res.sendFile(path.join(distPath, "index.html"));
        });
        app.get("/creator/syllabus", (req, res) => {
            res.sendFile(path.join(distPath, "index.html"));
        });
        app.get("/creator/:file", (req, res) => {
            console.log("GET /creator/:file", req.params.file);
            const file = path.join(distPath, req.params.file);
            res.sendFile(file);
        });
        app.get("/creator/assets/:file", (req, res) => {
            const file = path.join(distPath, "assets", req.params.file);
            res.sendFile(file);
        });
        app.post("/actions/create-course", async (req, res) => {
            console.log("POST /actions/create-course");
            const { syllabus } = req.body;
            const rigoToken = req.header("x-rigo-token");
            const bcToken = req.header("x-breathecode-token");
            if (!rigoToken || !bcToken) {
                return res.status(400).json({ error: "Missing tokens" });
            }
            const courseSlug = syllabus.courseInfo.slug;
            const tutorialDir = `courses/${courseSlug}`;
            const learnJson = (0, exports.createLearnJson)(syllabus.courseInfo);
            try {
                await api_1.default.createRigoPackage(rigoToken, courseSlug, learnJson);
            }
            catch (error) {
                console.error("Failed to create Rigo package:", error);
                return res.status(400).json({ error: "Failed to create Rigo package" });
            }
            try {
                await uploadFileToBucket(bucket, JSON.stringify(learnJson), `${tutorialDir}/learn.json`);
            }
            catch (error) {
                console.error("Failed to upload learn.json:", error);
                return res.status(400).json({ error: "Failed to upload learn.json" });
            }
            console.log("🔄 Learn.json and initialSyllabus.json uploaded to bucket to", tutorialDir);
            for (let i = 0; i < syllabus.lessons.length; i++) {
                const lesson = syllabus.lessons[i];
                const exSlug = (0, creatorUtilities_2.slugify)(lesson.id + "-" + lesson.title);
                const targetDir = `${tutorialDir}/exercises/${exSlug}`;
                // eslint-disable-next-line no-await-in-loop
                await uploadInitialReadme(bucket, exSlug, targetDir, syllabus.courseInfo);
            }
            const sidebar = await createInitialSidebar(syllabus.lessons.map(lesson => (0, creatorUtilities_2.slugify)(lesson.id + "-" + lesson.title)), syllabus.courseInfo.language);
            const initialSyllabus = Object.assign(Object.assign({}, syllabus), { lessons: syllabus.lessons.map((lesson, index) => {
                    if (index < 1) {
                        return Object.assign(Object.assign({}, lesson), { generated: false, status: "GENERATING" });
                    }
                    return Object.assign(Object.assign({}, lesson), { generated: false, status: "PENDING" });
                }) });
            await uploadFileToBucket(bucket, JSON.stringify(initialSyllabus), `${tutorialDir}/.learn/initialSyllabus.json`);
            await uploadFileToBucket(bucket, JSON.stringify(sidebar), `${tutorialDir}/.learn/sidebar.json`);
            const firstLesson = syllabus.lessons[0];
            const lastResult = "---";
            // Use new two-phase generation workflow
            const completionId = await startInitialContentGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, firstLesson, courseSlug, syllabus.courseInfo.purpose, lastResult);
            if (firstLesson) {
                firstLesson.translations = {
                    [syllabus.courseInfo.language || "en"]: {
                        completionId,
                        startedAt: Date.now(),
                        completedAt: 0,
                    },
                };
            }
            await createInitialReadme(JSON.stringify(syllabus.courseInfo), courseSlug, rigoToken);
            return res.json({
                message: "Course generation started",
                slug: syllabus.courseInfo.slug,
            });
        });
        app.get("/courses/:courseSlug/syllabus", async (req, res) => {
            try {
                console.log("GET /courses/:courseSlug/syllabus");
                const { courseSlug } = req.params;
                const syllabus = await bucket.file(`courses/${courseSlug}/.learn/initialSyllabus.json`);
                const [content] = await syllabus.download();
                const syllabusJson = JSON.parse(content.toString());
                res.json(syllabusJson);
            }
            catch (error) {
                console.error("Error getting syllabus:", error);
                return res.status(500).json({ error: "Error getting syllabus" });
            }
        });
        app.get("/courses/:courseSlug/exercises/:exerciseSlug/", async (req, res) => {
            var _a;
            console.log("GET /courses/:courseSlug/exercises/:exerciseSlug/");
            const { courseSlug, exerciseSlug } = req.params;
            try {
                const prefix = `courses/${courseSlug}/exercises/${exerciseSlug}/`;
                const [files] = await bucket.getFiles({ prefix });
                if (files.length === 0) {
                    return res.status(404).json({ error: "Exercise not found" });
                }
                const fileList = files.map(file => ({
                    name: path.basename(file.name),
                    path: file.name,
                }));
                const entryFile = (_a = fileList.find(f => /^index\.\w+$/.test(f.name) || /^app\.\w+$/.test(f.name))) === null || _a === void 0 ? void 0 : _a.name;
                const langMatch = entryFile === null || entryFile === void 0 ? void 0 : entryFile.match(/\.(js|ts|py|java|cpp|html|rb)/);
                const language = langMatch ? langMatch[1] : null;
                const isTesteable = Boolean(entryFile);
                return res.json({
                    slug: exerciseSlug,
                    graded: isTesteable,
                    entry: entryFile || null,
                    language,
                    files: fileList,
                });
            }
            catch (error) {
                console.error("❌ Error fetching exercise info:", error);
                return res.status(500).json({ error: error.message });
            }
        });
        app.post("/actions/publish/:slug", async (req, res) => {
            try {
                const { slug } = req.params;
                const { currentLanguage } = req.body;
                const rigoToken = req.header("x-rigo-token");
                const bcToken = req.header("x-breathecode-token");
                // const { academyId, categoryId } = req.body
                if (!rigoToken || !bcToken) {
                    return res
                        .status(400)
                        .json({ error: "Authentication failed, missing tokens" });
                }
                const { config, exercises } = await (0, configBuilder_1.buildConfig)(bucket, slug);
                const prefix = `courses/${slug}/`;
                const tmpRoot = path.join(os.tmpdir(), `learnpack-${slug}`);
                const buildRoot = path.join(tmpRoot, "build");
                rimraf.sync(tmpRoot);
                mkdirp.sync(buildRoot);
                // 4) Copiar UI descomprimida (_app)
                const uiSrc = path.resolve(__dirname, "../ui/_app");
                const copyDir = (src, dest) => {
                    for (const ent of fs.readdirSync(src, { withFileTypes: true })) {
                        const from = path.join(src, ent.name);
                        const to = path.join(dest, ent.name);
                        if (ent.isDirectory()) {
                            mkdirp.sync(to);
                            copyDir(from, to);
                        }
                        else {
                            fs.copyFileSync(from, to);
                        }
                    }
                };
                copyDir(uiSrc, buildRoot);
                const availableLangs = Object.keys(config.title);
                let selectedLang = "en";
                let title = "";
                if (availableLangs.includes("en")) {
                    title = config.title.en;
                }
                else {
                    // Select the first available lang
                    title = config.title[availableLangs[0]];
                    selectedLang = availableLangs[0];
                }
                const idxTpl = fs.readFileSync(path.join(uiSrc, "index.html"), "utf-8");
                const idxHtml = idxTpl
                    .replace(/{{title}}/g, title)
                    .replace(/<title>.*<\/title>/, `<title>${title}</title>`)
                    .replace(/{{description}}/g, config.description[selectedLang])
                    .replace(/{{preview}}/g, fixPreviewUrl(slug, "") ||
                    "https://raw.githubusercontent.com/learnpack/ide/master/public/learnpack.svg")
                    .replace(/{{slug}}/g, slug)
                    .replace(/{{duration}}/g, (0, misc_1.minutesToISO8601Duration)(config.duration));
                fs.writeFileSync(path.join(buildRoot, "index.html"), idxHtml);
                const mfTpl = fs.readFileSync(path.join(uiSrc, "manifest.webmanifest"), "utf-8");
                const mf = mfTpl
                    .replace(/{{course_title}}/g, config.title.us)
                    .replace(/{{course_app_name}}/g, config.slug);
                fs.writeFileSync(path.join(buildRoot, "manifest.webmanifest"), mf);
                // 7) Descargar todos los archivos del bucket al buildRoot
                const [files] = await bucket.getFiles({ prefix });
                await Promise.all(files.map(async (file) => {
                    const rel = file.name.replace(prefix, "");
                    const out = path.join(buildRoot, rel);
                    mkdirp.sync(path.dirname(out));
                    const [buf] = await file.download();
                    fs.writeFileSync(out, buf);
                }));
                // 8) Crear el config.json en buildRoot con lo que retorna buildConfig
                const fullConfig = { config, exercises };
                fullConfig.config.slug = slug;
                fullConfig.config.preview =
                    fixPreviewUrl(slug, "") ||
                        "https://raw.githubusercontent.com/learnpack/ide/master/public/learnpack.svg";
                fs.writeFileSync(path.join(buildRoot, "config.json"), JSON.stringify(fullConfig, null, 2), "utf-8");
                // 9) Empaquetar en ZIP (solo contenido de buildRoot)
                const zipName = `${slug}.zip`;
                const zipPath = path.join(tmpRoot, zipName);
                const output = fs.createWriteStream(zipPath);
                const archive = archiver("zip", { zlib: { level: 9 } });
                output.on("close", async () => {
                    // 10) Subir ZIP a RigoBot
                    const form = new FormData();
                    form.append("file", fs.createReadStream(zipPath));
                    form.append("config", JSON.stringify(config));
                    const rigoRes = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/learnpack/upload`, form, {
                        headers: Object.assign(Object.assign({}, form.getHeaders()), { Authorization: "Token " + rigoToken.trim() }),
                    });
                    await createMultiLangAsset(bucket, rigoToken, bcToken, slug, fullConfig.config, rigoRes.data.url);
                    rimraf.sync(tmpRoot);
                    console.log("RigoRes", rigoRes.data);
                    return res.json({ url: rigoRes.data.url });
                });
                archive.on("error", err => {
                    console.error("ZIP Error:", err);
                    return res.status(500).send("Error building zip");
                });
                archive.pipe(output);
                archive.directory(buildRoot, false);
                await archive.finalize();
            }
            catch (error) {
                console.error(error);
                return res.status(500).json({ error: error.message });
            }
        });
        app.get("/courses/:courseSlug/exercises/:exerciseSlug/file/:filename", async (req, res) => {
            console.log("GET /courses/:courseSlug/exercises/:exerciseSlug/file/:filename", req.params);
            const { courseSlug, exerciseSlug, filename } = req.params;
            try {
                const filePath = `courses/${courseSlug}/exercises/${exerciseSlug}/${filename}`;
                const file = bucket.file(filePath);
                const [exists] = await file.exists();
                if (!exists) {
                    return res.status(404).json({ error: "File not found" });
                }
                const [content] = await file.download();
                res.set("Content-Type", "text/plain");
                return res.send(content.toString("utf-8"));
            }
            catch (error) {
                console.error("❌ Error fetching file:", error);
                return res.status(500).json({
                    error: error.message || "Unable to fetch file",
                });
            }
        });
        const YT_REGEX = /(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]{11})/;
        app.get("/actions/fetch/:link", async (req, res) => {
            const { link } = req.params;
            console.log("[DEBUG] FETCHING LINK", link);
            try {
                // 1) Decode the URL
                const decoded = buffer_1.Buffer.from(link, "base64url").toString("utf-8");
                const ytMatch = decoded.match(YT_REGEX);
                console.log("[DEBUG] YT MATCH", ytMatch);
                if (ytMatch) {
                    const videoId = ytMatch[1];
                    const resFromRigo = await axios_1.default.get(`${api_1.RIGOBOT_REALTIME_HOST}/actions/youtube-transcript/${videoId}`);
                    console.log("[DEBUG] RES FROM RIGO", resFromRigo.data);
                    const transcript = resFromRigo.data.transcript;
                    return res.json({
                        url: decoded,
                        title: resFromRigo.data.title || null,
                        author: resFromRigo.data.author || null,
                        transcript,
                    });
                }
                const response = await axios_1.default.get(decoded, { responseType: "text" });
                const html = response.data;
                const title = getTitleFromHTML(html);
                console.log("TITLE", title);
                const text = (0, html_to_text_1.convert)(html);
                console.log("[DEBUG] TEXT", text);
                return res.json({
                    url: decoded,
                    text,
                    title,
                });
            }
            catch (error) {
                console.error("❌ /actions/fetch error:", error.message || error);
                res
                    .status(500)
                    .json({ error: error.message || "Failed to fetch link" });
            }
        });
        app.delete("/packages/:slug", async (req, res) => {
            console.log("DELETE /packages/:slug");
            const { slug } = req.params;
            const rigoToken = req.header("x-rigo-token");
            if (!rigoToken) {
                return res.status(400).json({ error: "x-rigo-token is required" });
            }
            const isValid = await (0, rigoActions_1.isValidRigoToken)(rigoToken);
            if (!isValid) {
                return res.status(400).json({ error: "Invalid RigoToken" });
            }
            const filePrefix = `courses/${slug}/`;
            try {
                const [files] = await bucket.getFiles({ prefix: filePrefix });
                if (files.length === 0) {
                    return res.status(200).json({
                        message: "No package found with the given slug, nothing to delete",
                    });
                }
                await Promise.all(files.map(file => file.delete()));
                console.log(`✅ Successfully deleted package from GCP: ${slug}`);
                return res.json({ message: `Package ${slug} deleted successfully` });
            }
            catch (error) {
                console.error("❌ Error deleting package:", error);
                return res.status(500).json({ error: "Failed to delete the package" });
            }
        });
        app.get("/proxy", async (req, res) => {
            const { url } = req.query;
            if (!url) {
                return res.status(400).json({ error: "URL is required" });
            }
            try {
                const decodedUrl = buffer_1.Buffer.from(url, "base64url").toString("utf-8");
                const response = await axios_1.default.get(decodedUrl, {
                    responseType: "arraybuffer",
                });
                res.set(response.headers);
                res.status(response.status).send(response.data);
            }
            catch (error) {
                console.error("Error in /proxy:", error);
                res.status(500).json({ error: "Failed to fetch the resource" });
            }
        });
        app.get("/memory-bank/:courseSlug", async (req, res) => {
            const { courseSlug } = req.params;
            const rigoToken = req.header("x-rigo-token");
            if (!rigoToken) {
                return res.status(400).json({
                    error: "Rigo token is required. x-rigo-token header is missing",
                });
            }
            const { isAuthor } = await (0, rigoActions_1.isPackageAuthor)(rigoToken, courseSlug);
            if (!isAuthor) {
                return res.status(403).json({
                    error: "You are not authorized to access the memory bank",
                });
            }
            try {
                const memoryBankPath = `courses/${courseSlug}/.learn/memory_bank.txt`;
                const memoryBankFile = bucket.file(memoryBankPath);
                const [exists] = await memoryBankFile.exists();
                if (!exists) {
                    return res.json({ content: "" });
                }
                const [content] = await memoryBankFile.download();
                res.json({ content: content.toString() });
            }
            catch (error) {
                console.error("❌ Error fetching memory bank:", error);
                res.status(500).json({ error: "Failed to fetch memory bank" });
            }
        });
        app.put("/memory-bank/:courseSlug", async (req, res) => {
            const { courseSlug } = req.params;
            const { content } = req.body;
            const rigoToken = req.header("x-rigo-token");
            if (!rigoToken) {
                return res.status(400).json({
                    error: "Rigo token is required. x-rigo-token header is missing",
                });
            }
            if (!content) {
                return res.status(400).json({ error: "Content is required" });
            }
            const { isAuthor } = await (0, rigoActions_1.isPackageAuthor)(rigoToken, courseSlug);
            if (!isAuthor) {
                return res.status(403).json({
                    error: "You are not authorized to update the memory bank",
                });
            }
            try {
                const memoryBankPath = `courses/${courseSlug}/.learn/memory_bank.txt`;
                await uploadFileToBucket(bucket, content, memoryBankPath);
                console.log(`✅ Memory bank updated for course: ${courseSlug}`);
                res.json({ message: "Memory bank updated successfully" });
            }
            catch (error) {
                console.error("❌ Error updating memory bank:", error);
                res.status(500).json({ error: "Failed to update memory bank" });
            }
        });
        // Export endpoint (POST) - supports both SCORM and EPUB
        app.post("/export/:course_slug/:format", async (req, res) => {
            const { course_slug, format } = req.params;
            const { language = "en", metadata } = req.body;
            try {
                let outputPath;
                let filename;
                switch (format) {
                    case "scorm": {
                        outputPath = await (0, export_1.exportToScorm)({
                            courseSlug: course_slug,
                            format: "scorm",
                            bucket,
                            outDir: path.join(__dirname, "../output/directory"),
                        });
                        filename = `${course_slug}-scorm.zip`;
                        break;
                    }
                    case "zip": {
                        outputPath = await (0, export_1.exportToZip)({
                            courseSlug: course_slug,
                            format: "zip",
                            bucket,
                            outDir: path.join(__dirname, "../output/directory"),
                        });
                        filename = `${course_slug}.zip`;
                        break;
                    }
                    case "epub": {
                        console.log("EPUB export", metadata);
                        // Validate required metadata for EPUB
                        if (!metadata ||
                            !metadata.creator ||
                            !metadata.publisher ||
                            !metadata.title ||
                            !metadata.rights ||
                            !metadata.lang) {
                            console.log("Missing required metadata for EPUB export", metadata);
                            return res.status(400).json({
                                error: "Missing required metadata for EPUB export",
                                required: ["creator", "publisher", "title", "rights", "lang"],
                            });
                        }
                        outputPath = await (0, export_1.exportToEpub)({
                            courseSlug: course_slug,
                            format: "epub",
                            bucket,
                            outDir: path.join(__dirname, "../output/directory"),
                            language: language,
                        }, metadata);
                        filename = `${course_slug}.epub`;
                        break;
                    }
                    default: {
                        return res.status(400).json({
                            error: "Invalid format. Supported formats: scorm, epub, zip",
                        });
                    }
                }
                // Send the file and clean up
                res.download(outputPath, filename, err => {
                    if (err)
                        console.error("Error sending file:", err);
                    // Clean up the generated file
                    if (fs.existsSync(outputPath)) {
                        fs.unlinkSync(outputPath);
                        if (format === "epub") {
                            const tempDir = path.dirname(outputPath);
                            if (fs.existsSync(tempDir)) {
                                rimraf.sync(tempDir);
                            }
                        }
                    }
                });
            }
            catch (error) {
                console.error("Export error:", error);
                res
                    .status(500)
                    .json({ error: "Export failed", details: error.message });
            }
        });
        server.listen(PORT, () => {
            console.log(`🚀 Creator UI server running at http://localhost:${PORT}/creator`);
        });
    }
}
ServeCommand.description = "Runs a small server to build tutorials";
ServeCommand.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" }), debug: command_1.flags.boolean({
        char: "d",
        description: "debugger mode for more verbage",
        default: false,
    }) });
exports.default = ServeCommand;
