"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processImage = exports.createLearnJson = void 0;
exports.processExercise = processExercise;
const command_1 = require("@oclif/command");
// import multer from "multer"
// import * as ytdl from "ytdl-core"
const youtube_transcript_1 = require("youtube-transcript");
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");
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 frontMatter = require("front-matter");
dotenv.config();
const createLearnJson = (courseInfo) => {
    console.log("courseInfo to create learn json", courseInfo);
    const expectedPreviewUrl = `https://${(0, creatorUtilities_2.slugify)(courseInfo.title)}.learn-pack.com/preview.png`;
    console.log("Preview url in generated learn.json", expectedPreviewUrl);
    const learnJson = {
        slug: (0, creatorUtilities_2.slugify)(courseInfo.title),
        title: {
            us: courseInfo.title,
        },
        technologies: courseInfo.technologies || [],
        difficulty: "beginner",
        description: {
            us: 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(file);
};
const uploadImageToBucket = async (bucket, url, path) => {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`Failed to download image: ${response.statusText}`);
    }
    const contentType = response.headers.get("content-type") || "application/octet-stream";
    const buffer = await response.arrayBuffer();
    const fileRef = bucket.file(path);
    await fileRef.save(Buffer.from(buffer), {
        resumable: false,
        contentType,
    });
};
const PARAMS = {
    expected_grade_level: "7",
    max_fkgl: 9,
    max_words: 200,
    max_rewrite_attempts: 2,
    max_title_length: 50,
};
const processImage = async (bucket, tutorialDir, url, description, rigoToken) => {
    try {
        const filename = (0, creatorUtilities_1.getFilenameFromUrl)(url);
        const imagePath = `${tutorialDir}/.learn/assets/${filename}`;
        console.log("🖼️ Generating image", imagePath);
        const res = await (0, rigoActions_1.generateImage)(rigoToken, { prompt: description });
        await uploadImageToBucket(bucket, res.image_url, imagePath);
        console.log("✅ Image", imagePath, "generated successfully!");
        return true;
    }
    catch (_a) {
        return false;
    }
};
exports.processImage = processImage;
const createInitialSidebar = async (slugs) => {
    const sidebar = {};
    for (const slug of slugs) {
        sidebar[slug] = {
            us: slug,
        };
    }
    return sidebar;
};
async function processExercise(bucket, rigoToken, steps, packageContext, exercise, exercisesDir, courseSlug) {
    //   const tid = toast.loading("Generating lesson...")
    const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
    console.log("exSlug", exSlug);
    const readmeFilename = "README.md";
    const targetDir = `${exercisesDir}/${exSlug}`;
    console.log("✍🏻 Generating lesson", exercise.id, exercise.title);
    const isGeneratingText = `
  \`\`\`loader slug="${exSlug}"
  :rigo
  \`\`\`
      `;
    await uploadFileToBucket(bucket, isGeneratingText, `${targetDir}/${readmeFilename}`);
    const readme = await (0, rigoActions_1.readmeCreator)(rigoToken, {
        title: `${exercise.id} - ${exercise.title}`,
        output_lang: "en",
        list_of_exercises: JSON.stringify(steps),
        tutorial_description: packageContext,
        lesson_description: exercise.description,
        kind: exercise.type.toLowerCase(),
    });
    const duration = exercise.duration;
    let attempts = 0;
    let readability = (0, creatorUtilities_2.checkReadability)(readme.parsed.content, 200, duration || 1);
    while (readability.fkglResult.fkgl > PARAMS.max_fkgl &&
        attempts < PARAMS.max_rewrite_attempts) {
        (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
            lesson: exSlug,
            status: "generating",
            log: `🔄 The lesson ${exercise.id} - ${exercise.title} has a readability score of ${readability.fkglResult.fkgl}`,
        });
        // eslint-disable-next-line
        const reducedReadme = await (0, rigoActions_1.makeReadmeReadable)(rigoToken, {
            lesson: readability.body,
            number_of_words: readability.minutes.toString(),
            expected_number_words: PARAMS.max_words.toString(),
            fkgl_results: JSON.stringify(readability.fkglResult),
            expected_grade_level: PARAMS.expected_grade_level,
        });
        if (!reducedReadme)
            break;
        readability = (0, creatorUtilities_2.checkReadability)(reducedReadme.parsed.content, PARAMS.max_words, duration || 1);
        attempts++;
    }
    await uploadFileToBucket(bucket, readability.newMarkdown, `${targetDir}/${readmeFilename}`);
    (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
        lesson: exSlug,
        status: "done",
        log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
    });
    if (exercise.type.toLowerCase() === "code") {
        console.log("🔍 Creating code file for", exercise.title);
        const codeFile = await (0, rigoActions_1.createCodeFile)(rigoToken, {
            readme: readability.newMarkdown,
            tutorial_info: packageContext,
        });
        await uploadFileToBucket(bucket, codeFile.parsed.content, `${targetDir}/index.${codeFile.parsed.extension.replace(".", "")}`);
        (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
            lesson: exSlug,
            status: "done",
            log: `✅ The code file for ${exercise.title} has been generated successfully!`,
        });
    }
    return readability.newMarkdown;
}
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);
        }
        const credentials = JSON.parse(crendsEnv);
        const bucketStorage = new storage_1.Storage({
            credentials,
        });
        const bucket = bucketStorage.bucket(process.env.GCP_BUCKET_NAME || "learnpack-packages");
        // 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());
        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.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);
        });
        app.post("/upload-image", express.json({ limit: "10mb" }), async (req, res) => {
            const { image_url, destination } = req.body;
            if (!image_url || !destination) {
                return res
                    .status(400)
                    .json({ error: "image_url and destination are required" });
            }
            try {
                const response = await fetch(image_url);
                if (!response.ok) {
                    return res.status(400).json({
                        error: `Failed to download image: ${response.statusText}`,
                    });
                }
                const contentType = response.headers.get("content-type") || "application/octet-stream";
                const buffer = await response.arrayBuffer();
                const file = bucket.file(destination);
                await file.save(Buffer.from(buffer), {
                    resumable: false,
                    contentType,
                });
                console.log(`✅ Image uploaded to ${file.name}`);
                res.json({ message: "Image uploaded successfully", path: file.name });
            }
            catch (error) {
                console.error("❌ upload-image error:", error);
                res.status(500).json({ error: error.message });
            }
        });
        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.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.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 {
                console.log("GET CONFIG, COURSE SLUG", courseSlug);
                const { config, exercises } = await (0, configBuilder_1.buildConfig)(bucket, courseSlug);
                res.set("X-Creator-Web", "true");
                res.set("Access-Control-Expose-Headers", "X-Creator-Web");
                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 || "us";
            const basePath = `courses/${courseSlug}/exercises/${slug}/`;
            const filename = lang === "us" ? "README.md" : `README.${lang}.md`;
            const file = bucket.file(basePath + filename);
            let contentBuffer;
            try {
                contentBuffer = await file.download();
            }
            catch (_a) {
                if (lang !== "us") {
                    console.warn(`No README for lang '${lang}', falling back to 'us'`);
                    const fallbackFile = bucket.file(basePath + "README.md");
                    contentBuffer = await fallbackFile.download();
                }
                else {
                    return res.status(404).json({ error: "README not found" });
                }
            }
            const { attributes, body } = frontMatter(contentBuffer[0].toString());
            res.send({ attributes, body });
        });
        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${language === "us" || language === "en" ? "" : `.${language}`}.md`;
            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 } = req.body;
            const query = req.query;
            const courseSlug = query.slug;
            console.log("EXERCISE SLUGS", exerciseSlugs);
            console.log("LANGUAGES", languages);
            console.log("RIGO TOKEN", rigoToken);
            if (!rigoToken) {
                return res.status(400).json({ error: "RigoToken not found" });
            }
            const languagesToTranslate = languages.split(",");
            try {
                await Promise.all(exerciseSlugs.map(async (slug) => {
                    const readmePath = `courses/${courseSlug}/exercises/${slug}/README.md`;
                    const readme = await bucket.file(readmePath).download();
                    await Promise.all(languagesToTranslate.map(async (language) => {
                        const response = await (0, rigoActions_1.translateExercise)(rigoToken, {
                            text_to_translate: readme.toString(),
                            output_language: language,
                            exercise_slug: slug,
                        });
                        const translatedReadme = await bucket.file(`courses/${courseSlug}/exercises/${slug}/README.${response.parsed.output_language_code}.md`);
                        await translatedReadme.save(response.parsed.translation);
                    }));
                }));
                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) => {
            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) {
                    console.log("SIDEBAR FILE NOT FOUND", courseSlug);
                    const { exercises } = await (0, configBuilder_1.buildConfig)(bucket, courseSlug);
                    const exerciseSlugsArray = exercises.map(exercise => exercise.slug);
                    const sidebar = await createInitialSidebar(exerciseSlugsArray);
                    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`);
                        }
                        await uploadFileToBucket(bucket, JSON.stringify(sidebar), `courses/${courseSlug}/.learn/sidebar.json`);
                        return res.status(200).json(fixedSidebar);
                    }
                    await uploadFileToBucket(bucket, JSON.stringify(sidebar), `courses/${courseSlug}/.learn/sidebar.json`);
                    return res.status(200).json(sidebar);
                }
                console.error("Unexpected error:", error);
                res.status(500).json({ error: error.message });
            }
        });
        app.get("/test/:slug", (req, res) => {
            (0, creatorSocket_1.emitToCourse)(req.params.slug, "course-creation", {
                lesson: "000-welcome-to-bird-domestication",
                status: "generating",
                log: "Hello",
            });
            (0, creatorSocket_1.emitToCourse)(req.params.slug, "course-creation", {
                lesson: "000-welcome-to-bird-domestication",
                status: "generating",
                log: "Hello",
            });
            (0, creatorSocket_1.emitToCourse)(req.params.slug, "course-creation", {
                lesson: "000-welcome-to-bird-domestication",
                status: "generating",
                log: "Hello broder",
            });
            (0, creatorSocket_1.emitToCourse)(req.params.slug, "course-creation", {
                lesson: "000-welcome-to-bird-domestication",
                status: "done",
                log: "Hello broder",
            });
            (0, creatorSocket_1.emitToCourse)(req.params.slug, "course-creation", {
                lesson: "other",
                status: "generating",
                log: "Hello",
            });
            res.send({ message: "Course creation started" });
        });
        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 slug = (0, creatorUtilities_2.slugify)(syllabus.courseInfo.title);
            const tutorialDir = `courses/${slug}`;
            const learnJson = (0, exports.createLearnJson)(syllabus.courseInfo);
            try {
                await api_1.default.createRigoPackage(rigoToken, slug, learnJson);
            }
            catch (error) {
                console.error("Failed to create Rigo package:", error);
                return res.status(400).json({ error: "Failed to create Rigo package" });
            }
            await uploadFileToBucket(bucket, JSON.stringify(learnJson), `${tutorialDir}/learn.json`);
            console.log("🔄 Learn.json uploaded to bucket to", tutorialDir);
            const lessonsPromises = syllabus.lessons.map(lesson => processExercise(bucket, rigoToken, syllabus.lessons, JSON.stringify(syllabus.courseInfo), lesson, tutorialDir + "/exercises", (0, creatorUtilities_2.slugify)(syllabus.courseInfo.title)));
            const readmeContents = await Promise.all(lessonsPromises);
            let imagesArray = [];
            for (const content of readmeContents) {
                imagesArray = [...imagesArray, ...(0, creatorUtilities_1.extractImagesFromMarkdown)(content)];
            }
            console.log("📷 Generating images...");
            const imagePromises = imagesArray.map(async (image) => {
                return (0, exports.processImage)(bucket, tutorialDir, image.url, image.alt, rigoToken);
            });
            await Promise.all(imagePromises);
            const sidebar = await createInitialSidebar(syllabus.lessons.map(lesson => (0, creatorUtilities_2.slugify)(lesson.id + "-" + lesson.title)));
            await uploadFileToBucket(bucket, JSON.stringify(sidebar), `${tutorialDir}/.learn/sidebar.json`);
            console.log("SIDEBAR UPLOADED", sidebar);
            return res.json({
                message: "Course created",
                slug: (0, creatorUtilities_2.slugify)(syllabus.courseInfo.title),
            });
        });
        app.post("/actions/publish/:slug", async (req, res) => {
            try {
                const { slug } = req.params;
                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: "Faltan tokens o academy/category" });
                }
                const { config, exercises } = await (0, configBuilder_1.buildConfig)(bucket, slug);
                // 3) Preparar dirs temporales
                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);
                // 5) Inyectar placeholders en index.html
                const idxTpl = fs.readFileSync(path.join(uiSrc, "index.html"), "utf-8");
                const idxHtml = idxTpl
                    .replace(/{{title}}/g, config.title.us)
                    .replace(/<title>.*<\/title>/, `<title>${config.title.us}</title>`)
                    .replace(/{{description}}/g, config.description.us)
                    .replace(/{{preview}}/g, fixPreviewUrl(slug, config.preview) ||
                    "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 };
                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}` }),
                    });
                    await (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken }, config, rigoRes.data.url);
                    rimraf.sync(tmpRoot);
                    console.log("RIGO RES", 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 });
            }
        });
        const YT_REGEX = /(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]{11})/;
        app.get("/actions/fetch/:link", async (req, res) => {
            const { link } = req.params;
            try {
                // 1) Decode the URL
                const decoded = Buffer.from(link, "base64url").toString("utf-8");
                const ytMatch = decoded.match(YT_REGEX);
                if (ytMatch) {
                    const videoId = ytMatch[1];
                    // fetch metadata
                    const items = await youtube_transcript_1.YoutubeTranscript.fetchTranscript(videoId);
                    const transcript = items.map(i => i.text).join(" ");
                    const { data: meta } = await axios_1.default.get("https://www.youtube.com/oembed", {
                        params: { url: decoded, format: "json" },
                    });
                    return res.json({
                        url: decoded,
                        title: meta.title,
                        author: meta.author_name,
                        thumbnail: meta.thumbnail_url,
                        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);
                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.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" });
            }
        });
        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;
