"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurrentTechnologies = exports.fetchTechnologies = exports.doesAssetExists = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_REALTIME_HOST = exports.RIGOBOT_HOST = void 0;
const console_1 = require("../utils/console");
const storage = require("node-persist");
const cli_ux_1 = require("cli-ux");
const axios_1 = require("axios");
const dotenv = require("dotenv");
dotenv.config();
const HOST = "https://breathecode.herokuapp.com";
exports.RIGOBOT_HOST = "https://rigobot.herokuapp.com";
exports.RIGOBOT_REALTIME_HOST = "https://chat.4geeks.com";
// export const RIGOBOT_HOST = "https://rigobot-test-cca7d841c9d8.herokuapp.com"
// export const RIGOBOT_HOST =
// "https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us118.gitpod.io"
// eslint-disable-next-line
const _fetch = require("node-fetch");
const fetch = async (url, options = {}, returnAsJson = true) => {
    const headers = { "Content-Type": "application/json" };
    console_1.default.debug(`Fetching ${url}`);
    let session = null;
    try {
        session = await storage.getItem("bc-payload");
        if (session.token && session.token !== "" && !url.includes("/token"))
            headers.Authorization = "Token " + session.token;
    }
    catch (_a) { }
    try {
        const resp = await _fetch(url, Object.assign(Object.assign({}, options), { headers: Object.assign(Object.assign({}, headers), options.headers) }));
        if (resp.status >= 200 && resp.status < 300) {
            return returnAsJson ? await resp.json() : await resp.text();
        }
        if (resp.status === 401)
            console_1.default.debug("Invalid authentication credentials", `Code: 401`);
        // throw APIError("Invalid authentication credentials", 401)
        else if (resp.status === 404)
            throw APIError("Package not found", 404);
        else if (resp.status >= 500)
            throw APIError("Impossible to connect with the server", 500);
        else if (resp.status >= 400) {
            const error = await resp.json();
            if (error.detail || error.error) {
                throw APIError(error.detail || error.error);
            }
            else if (error.nonFieldErrors) {
                throw APIError(error.nonFieldErrors[0], error);
            }
            else if (typeof error === "object") {
                if (Object.keys(error).length > 0) {
                    const key = error[Object.keys(error)[0]];
                    throw APIError(`${key}: ${error[key][0]}`, error);
                }
            }
            else {
                throw APIError("Uknown error");
            }
        }
        else
            throw APIError("Uknown error");
    }
    catch (error) {
        console_1.default.error(error.message);
        throw error;
    }
};
const login = async (identification, password) => {
    try {
        cli_ux_1.default.action.start(`Looking for credentials with ${identification}`);
        await cli_ux_1.default.wait(1000);
        const url = `${HOST}/v1/auth/login/`;
        const res = await axios_1.default.post(url, {
            email: identification,
            password: password,
        });
        const data = res.data;
        cli_ux_1.default.action.stop("ready");
        let rigoPayload = null;
        try {
            rigoPayload = await loginRigo(data.token);
        }
        catch (_a) {
            return Object.assign(Object.assign({}, data), { rigobot: null });
        }
        return Object.assign(Object.assign({}, data), { rigobot: rigoPayload });
    }
    catch (error) {
        cli_ux_1.default.action.stop("error");
        console_1.default.error(error.message);
        console_1.default.debug(error);
    }
};
const loginRigo = async (token) => {
    try {
        const rigoUrl = `${exports.RIGOBOT_HOST}/v1/auth/me/token?breathecode_token=${token}`;
        const rigoResp = await _fetch(rigoUrl);
        const rigobotJson = await rigoResp.json();
        return rigobotJson;
    }
    catch (error) {
        // Handle the error as needed, for example log it or return a custom error message
        console_1.default.error("Error logging in to Rigo, did you already accepted Rigobot?:", error);
        throw new Error("Failed to log in to Rigo");
    }
};
const publish = async (config) => {
    const keys = [
        "difficulty",
        "language",
        "skills",
        "technologies",
        "slug",
        "repository",
        "author",
        "title",
    ];
    const payload = {};
    for (const k of keys)
        config[k] ? (payload[k] = config[k]) : null;
    try {
        console.log("Package to publish:", payload);
        cli_ux_1.default.action.start("Updating package information...");
        await cli_ux_1.default.wait(1000);
        const data = await fetch(`${HOST}/v1/package/${config.slug}`, {
            method: "PUT",
            body: JSON.stringify(payload),
        });
        cli_ux_1.default.action.stop("ready");
        return data;
    }
    catch (error) {
        console_1.default.error(error.message);
        console_1.default.debug(error);
        throw error;
    }
};
const update = async (config) => {
    try {
        cli_ux_1.default.action.start("Updating package information...");
        await cli_ux_1.default.wait(1000);
        const data = await fetch(`${HOST}/v1/package/`, {
            method: "POST",
            body: JSON.stringify(config),
        });
        cli_ux_1.default.action.stop("ready");
        return data;
    }
    catch (error) {
        console_1.default.error(error.message);
        console_1.default.debug(error);
        throw error;
    }
};
const getPackage = async (slug) => {
    try {
        cli_ux_1.default.action.start("Downloading package information...");
        await cli_ux_1.default.wait(1000);
        const data = await fetch(`${HOST}/v1/package/${slug}`);
        cli_ux_1.default.action.stop("ready");
        return data;
    }
    catch (error) {
        if (error.status === 404)
            console_1.default.error(`Package ${slug} does not exist`);
        else
            console_1.default.error(`Package ${slug} does not exist`);
        console_1.default.debug(error);
        throw error;
    }
};
const getLangs = async () => {
    try {
        cli_ux_1.default.action.start("Downloading language options...");
        await cli_ux_1.default.wait(1000);
        const data = await fetch(`${HOST}/v1/package/language`);
        cli_ux_1.default.action.stop("ready");
        return data;
    }
    catch (error) {
        if (error.status === 404)
            console_1.default.error("Package slug does not exist");
        else
            console_1.default.error("Package slug does not exist");
        console_1.default.debug(error);
        throw error;
    }
};
const getAllPackages = async ({ lang = "", slug = "", }) => {
    try {
        cli_ux_1.default.action.start("Downloading packages...");
        await cli_ux_1.default.wait(1000);
        const data = await fetch(`${HOST}/v1/package/all?limit=100&language=${lang}&slug=${slug}`);
        cli_ux_1.default.action.stop("ready");
        return data;
    }
    catch (error) {
        console_1.default.error(`Package ${slug} does not exist`);
        console_1.default.debug(error);
        throw error;
    }
};
const APIError = (error, code) => {
    const message = error.message || error;
    const _err = new Error(message);
    _err.status = code || 400;
    return _err;
};
const sendBatchTelemetry = async function (url, body) {
    if (!url) {
        return;
    }
    const session = await storage.getItem("bc-payload");
    if (!session ||
        !Object.prototype.hasOwnProperty.call(session, "token") ||
        session.token === "") {
        console_1.default.debug("No token found, skipping batch telemetry delivery");
        return;
    }
    if (!session || !session.user_id || session.user_id === "") {
        console_1.default.debug("No user_id found, skipping batch telemetry delivery");
        return;
    }
    body.user_id = session.user_id;
    fetch(url, {
        method: "POST",
        body: JSON.stringify(body),
    }, false)
        .then(response => {
        console_1.default.debug("Telemetry sent successfully");
        return response;
    })
        .catch(error => {
        console_1.default.debug("Error while sending batch Telemetry", error);
    });
};
const sendStreamTelemetry = async function (url, body) {
    if (!url) {
        return;
    }
    const session = await storage.getItem("bc-payload");
    if (!session ||
        !Object.prototype.hasOwnProperty.call(session, "token") ||
        session.token === "") {
        console_1.default.debug("No token found, skipping stream telemetry delivery");
        return;
    }
    fetch(url, {
        method: "POST",
        body: JSON.stringify(body),
    }, false)
        .then(response => {
        return response;
    })
        .catch(error => {
        console_1.default.debug("Error while sending stream Telemetry", error);
    });
};
const countConsumables = (consumables, consumableSlug = "ai-tutorial-generation") => {
    // Find the void that matches the consumableSlug
    const consumable = consumables.voids.find((voidItem) => voidItem.slug === consumableSlug);
    // Return the available units or 0 if not found
    return consumable ? consumable.balance.unit : 0;
};
exports.countConsumables = countConsumables;
const getConsumable = async (token, consumableSlug = "ai-generation") => {
    const url = `${HOST}/v1/payments/me/service/consumable?virtual=true`;
    const headers = {
        Authorization: `Token ${token}`,
    };
    try {
        const response = await axios_1.default.get(url, { headers });
        const count = (0, exports.countConsumables)(response.data, consumableSlug);
        return { count };
    }
    catch (error) {
        console.error("Error fetching consumables:", error);
        throw error;
    }
};
exports.getConsumable = getConsumable;
const neededPermissions = [
    "add_asset",
    "change_asset",
    "view_asset",
    "delete_asset",
];
const listUserAcademies = async (breathecodeToken) => {
    const url = "https://breathecode.herokuapp.com/v1/auth/user/me";
    try {
        const response = await axios_1.default.get(url, {
            headers: {
                Authorization: `Token ${breathecodeToken}`,
            },
        });
        const data = response.data;
        const academiesMap = new Map();
        for (const role of data.roles) {
            const academy = role.academy;
            if (!academiesMap.has(academy.id)) {
                academiesMap.set(academy.id, academy);
            }
        }
        const permissions = new Set(data.permissions.map((p) => p.codename));
        // Validate if the user has ALL the needed permissions
        const hasAllPermissions = neededPermissions.every(permission => permissions.has(permission));
        if (!hasAllPermissions) {
            // The user does not have all the needed permissions
            return [];
        }
        return [...academiesMap.values()];
    }
    catch (error) {
        console.error("Failed to fetch user academies:", error);
        return [];
    }
};
exports.listUserAcademies = listUserAcademies;
const validateToken = async (token) => {
    const url = "https://breathecode.herokuapp.com/v1/auth/user/me";
    const headers = {
        Authorization: `Token ${token}`,
    };
    try {
        const response = await axios_1.default.get(url, { headers });
        return response.data;
    }
    catch (_a) {
        return false;
    }
};
exports.validateToken = validateToken;
const createAsset = async (token, asset) => {
    const body = {
        slug: asset.slug,
        title: asset.title,
        lang: asset.lang,
        asset_type: "EXERCISE",
        visibility: "PUBLIC",
        status: "DRAFT",
        url: asset.url,
        readme_url: null,
        difficulty: null,
        duration: null,
        graded: false,
        gitpod: true,
        category: asset.category,
        owner: asset.owner,
        author: asset.author,
        preview: asset.preview,
        description: asset.description,
        external: false,
        interactive: true,
        solution_video_url: null,
        intro_video_url: null,
        translations: [asset.lang],
        learnpack_deploy_url: asset.learnpack_deploy_url,
        technologies: asset.technologies,
        readme_raw: asset.readme_raw,
        all_translations: asset.all_translations,
    };
    const url = `https://breathecode.herokuapp.com/v1/registry/asset/me`;
    const headers = {
        Authorization: `Token ${token}`,
    };
    try {
        const response = await axios_1.default.post(url, body, { headers });
        return response.data;
    }
    catch (error) {
        console.error("Failed to create asset:", error);
        throw error.response.data;
    }
};
exports.createAsset = createAsset;
const doesAssetExists = async (token, assetSlug) => {
    const url = `https://breathecode.herokuapp.com/v1/registry/asset/${assetSlug}`;
    const headers = {
        Authorization: `Token ${token}`,
    };
    try {
        const response = await axios_1.default.get(url, { headers });
        if (response.status === 200) {
            return { exists: true };
        }
        return { exists: false };
    }
    catch (_a) {
        // console.error("Failed to get asset:", error)
        return { exists: false };
    }
};
exports.doesAssetExists = doesAssetExists;
const updateAsset = async (token, assetSlug, asset) => {
    const url = `https://breathecode.herokuapp.com/v1/registry/asset/me/${assetSlug}`;
    const headers = {
        Authorization: `Token ${token}`,
    };
    try {
        const response = await axios_1.default.put(url, asset, { headers });
        return response.data;
    }
    catch (error) {
        console.error("Failed to update asset:", error);
        // Try to print the data
        // console.log(error.response.data)
        throw error.response.data;
    }
};
const getCategories = async (token) => {
    const url = `${HOST}/v1/registry/category`;
    const headers = {
        Authorization: `Token ${token}`,
    };
    try {
        const response = await axios_1.default.get(url, { headers });
        return response.data;
    }
    catch (error) {
        console.error("Failed to get categories:", error);
        throw error;
    }
};
const updateRigoAssetID = async (token, slug, asset_id) => {
    const cleanToken = token.replace(/[\n\r]/g, "");
    const url = `${exports.RIGOBOT_HOST}/v1/learnpack/package/${slug}/`;
    try {
        const response = await axios_1.default.put(url, { asset_id }, {
            headers: {
                Authorization: "Token " + cleanToken,
            },
        });
        return response.data;
    }
    catch (error) {
        console.error("Failed to update Rigo package:", error);
        throw error;
    }
};
const createRigoPackage = async (token, slug, config) => {
    const url = `${exports.RIGOBOT_HOST}/v1/learnpack/package`;
    const cleanToken = token.replace(/[\n\r]/g, "");
    try {
        const response = await axios_1.default.post(url, { slug, config }, {
            headers: {
                Authorization: "Token " + cleanToken,
            },
        });
        return response.data;
    }
    catch (error) {
        console.error("Failed to create Rigo package:", error);
        throw error;
    }
};
let technologiesCache = [];
const fetchTechnologies = async () => {
    const BREATHECODE_PERMANENT_TOKEN = process.env.BREATHECODE_PERMANENT_TOKEN;
    const LANGS = ["en", "es"];
    if (!BREATHECODE_PERMANENT_TOKEN) {
        throw new Error("BREATHECODE_PERMANENT_TOKEN is not defined in environment variables");
    }
    const headers = {
        Authorization: `Token ${BREATHECODE_PERMANENT_TOKEN}`,
    };
    const results = await Promise.all(LANGS.map(lang => axios_1.default
        .get(`${HOST}/v1/registry/technology?lang=${lang}`, { headers })
        .then(res => {
        return res.data;
    })
        .then(data => data.map((item) => ({
        slug: item.slug,
        lang: lang,
    })))));
    const allItems = results.flat();
    // Remove duplicates by slug+lang combination
    const unique = [];
    const seen = new Set();
    for (const item of allItems) {
        const key = `${item.slug}:${item.lang}`;
        if (!seen.has(key)) {
            seen.add(key);
            unique.push(item);
        }
    }
    return unique;
};
exports.fetchTechnologies = fetchTechnologies;
// Function to update the cache and schedule the next update
async function updateTechnologiesPeriodically() {
    try {
        technologiesCache = await (0, exports.fetchTechnologies)();
        // Uncomment for debugging:
        // console.log('Technologies list updated:', technologiesCache);
    }
    catch (error) {
        console.error("Error updating technologies list:", error);
    }
    finally {
        setTimeout(updateTechnologiesPeriodically, 24 * 60 * 60 * 1000);
    }
}
const getCurrentTechnologies = () => technologiesCache;
exports.getCurrentTechnologies = getCurrentTechnologies;
exports.default = {
    login,
    publish,
    update,
    getPackage,
    getLangs,
    getAllPackages,
    sendBatchTelemetry,
    sendStreamTelemetry,
    listUserAcademies: exports.listUserAcademies,
    validateToken: exports.validateToken,
    createAsset: exports.createAsset,
    doesAssetExists: exports.doesAssetExists,
    updateAsset,
    getCategories,
    updateRigoAssetID,
    createRigoPackage,
    getCurrentTechnologies: exports.getCurrentTechnologies,
    updateTechnologiesPeriodically,
};
