"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.lti_router = exports.post_form_message = void 0;
const service_common_1 = require("@crosslab/service-common");
const express_1 = require("express");
const express_async_handler_1 = __importDefault(require("express-async-handler"));
const config_1 = require("./config");
function post_form_message(url, message) {
    return `<html><body><form id="form" action="${url}" method="post">${Object.entries(message)
        .map(([key, value]) => `<input type="hidden" name="${key}" value="${value}">`)
        .join("")}</form><script>document.getElementById("form").submit()</script></body></html>`;
}
exports.post_form_message = post_form_message;
function lti_router(language, renderPage) {
    const router = (0, express_1.Router)();
    router.get("/lti/platforms", (0, express_async_handler_1.default)(async (req, res) => {
        const platforms = await req.apiClient.listPlatform();
        renderPage(req.path, language, res, req.user, { platforms });
    }));
    router.post("/lti/registration", (0, express_async_handler_1.default)(async (req, res) => {
        const platform = await req.apiClient.registerPlatform();
        const tool = {
            icon_url: config_1.config.BASE_URL + "/img/icon/android-chrome-512x512.png",
            base_url: config_1.config.BASE_URL + "/lti/launch?api=" + platform.launch_uri,
            jwks_url: config_1.config.BASE_URL + "/lti/jwks?api=" + platform.jwks_uri,
            login_url: config_1.config.BASE_URL + "/lti/login?api=" + platform.login_uri,
            redirect_urls: [
                config_1.config.BASE_URL + "/lti/launch?api=" + platform.launch_uri,
            ],
            parameters: [`registration_token=${platform.registration.token}`],
        };
        renderPage(req.path, language, res, req.user, { platform, tool });
    }));
    router.get("/lti/registration/status", (0, express_async_handler_1.default)(async (req, res) => {
        const platform = await req.apiClient.getPlatform(req.query.platform);
        res.send(platform.registration.state);
    }));
    router.get("/lti/jwks", (0, express_async_handler_1.default)(async (req, res) => {
        res.send(await req.apiClient.ltiJwks(req.query.api));
    }));
    router.post("/lti/login", (0, express_async_handler_1.default)(async (req, res) => {
        req.body.target_link_uri = config_1.config.BASE_URL + req.url.replaceAll("login", "launch"); // fix the target_link_uri to point to the launch endpoint
        const login = await req.apiClient.ltiLogin(req.query.api, req.body);
        res.status(302);
        res.header("Location", login.authentication_request_url);
        res.send();
    }));
    router.post("/lti/launch", (0, express_async_handler_1.default)(async (req, res) => {
        service_common_1.logger.info(req.query);
        const message = await req.apiClient.ltiLaunch(req.query.api, req.body);
        service_common_1.logger.info(message);
        req.lti = {
            isInstructor: message.session.roles.includes("instructor"),
            isStudent: message.session.roles.includes("student"),
            session: message.session,
            token: message.access_token,
        };
        renderPage("/lti/launch", language, res, req.user, { req });
    }));
    router.use("/lti", (0, express_async_handler_1.default)(async (req, _res, next) => {
        if (req.body.lti_session) {
            const session = JSON.parse(req.body.lti_session);
            req.lti = {
                session: session,
                isInstructor: session.roles.includes("instructor"),
                isStudent: session.roles.includes("student"),
                token: req.body.token,
            };
            req.initApiClient(req.lti.token);
        }
        next();
    }));
    const handle_experiment = async (req, res) => {
        if (!req.lti) {
            throw new Error("No LTI session found");
        }
        if (req.lti.session.experiment_uri) {
            const exp = await req.apiClient.updateExperiment(req.lti.session.experiment_uri, { status: "running" });
            const instances = [];
            for (const device of exp.instantiatedDevices ?? []) {
                if (device.url && device.token && device.codeUrl) {
                    instances.push({
                        codeUrl: device.codeUrl,
                        instanceUrl: device.url,
                        deviceToken: device.token,
                    });
                }
            }
            renderPage("/lti/experiment-show", language, res, req.user, {
                req,
                instances,
            });
        }
        else {
            renderPage("/lti/experiment-no-experiment", language, res, req.user, {
                req,
            });
        }
    };
    router.post("/lti/experiment_select", (0, express_async_handler_1.default)(async (req, res, next) => {
        if (!req.lti) {
            throw new Error("No LTI session found");
        }
        if (!req.lti.session.experiment_change_uri) {
            throw new Error("Experiment Change URI was not given - is the user an instructor?");
        }
        const resource = await req.apiClient.getResource(req.lti.session.resource_uri);
        if (req.body['action'] === 'start') {
            const _roles = Object.keys(req.body)
                .filter((k) => k.startsWith(`role_`))
                .map((k) => k.split("_")[1]);
            const _devices = _roles.map((r) => req.body[`role_${r}`]);
            const mapping = _devices.map((d, i) => ({ role: _roles[i], device: d }));
            const impersonate = req.body['impersonate'];
            if (impersonate) {
                await req.apiClient.updateLtiExperiment(req.lti.session.experiment_change_uri, { impersonate });
            }
            else if (mapping.length > 0) {
                await req.apiClient.updateLtiExperiment(req.lti.session.experiment_change_uri, { role_mapping: mapping });
            }
            return handle_experiment(req, res, next);
        }
        const template = await req.apiClient.getTemplate(resource.experiment_template_uri);
        const groupDevices = await Promise.all(template.configuration.devices.map((d) => req.apiClient.getDevice(d.device)));
        const students = resource.students_uri ? await req.apiClient.listResourceStudents(resource.students_uri) : [];
        const roles = [];
        const selectable_devices = new Set();
        for (let i = 0; i < groupDevices.length; i++) {
            const device = groupDevices[i];
            if (device.type === "group") {
                roles.push({
                    role: template.configuration.devices[i].role,
                    devices: device.devices.map((d) => d.url),
                });
                device.devices.forEach((d) => selectable_devices.add(d.url));
            }
        }
        const devices = Object.fromEntries(await Promise.all(Array.from(selectable_devices).map(async (d) => [
            d,
            await req.apiClient.getDevice(d),
        ])));
        renderPage(req.path, language, res, req.user, {
            req,
            resource,
            students: students.map((s) => ({ ...s, role_mapping: Object.fromEntries(s.role_mapping.map((r) => [r.role, r.device])) })),
            role_mapping: Object.fromEntries((req.lti.session.role_mapping ?? []).map((r) => [r.role, r.device])),
            roles,
            devices,
        });
    }));
    router.post("/lti/experiment", (0, express_async_handler_1.default)(handle_experiment));
    router.post("/lti/settings", (0, express_async_handler_1.default)(async (req, res) => {
        if (!req.lti) {
            throw new Error("No LTI session found");
        }
        const resource = await req.apiClient.getResource(req.lti.session.resource_uri);
        let experiment_template = undefined;
        try {
            experiment_template = await req.apiClient.getTemplate(resource.experiment_template_uri);
        }
        catch {
            // do nothing
        }
        renderPage(req.path, language, res, req.user, {
            req,
            resource,
            experiment_template,
        });
    }));
    router.post("/lti/settings_experiment", (0, express_async_handler_1.default)(async (req, res) => {
        if (!req.lti) {
            throw new Error("No LTI session found");
        }
        if (req.body.experiment_template_uri) {
            await req.apiClient.updateResource(req.lti.session.resource_uri, {
                experiment_template_uri: req.body.experiment_template_uri,
            });
        }
        const resource = await req.apiClient.getResource(req.lti.session.resource_uri);
        let experiment_template = undefined;
        try {
            experiment_template = await req.apiClient.getTemplate(resource.experiment_template_uri);
        }
        catch {
            // do nothing
        }
        const experiment_templates = await req.apiClient.listTemplate();
        renderPage(req.path, language, res, req.user, {
            req,
            resource,
            experiment_template,
            experiment_templates,
        });
    }));
    router.post("/lti/settings_device_map", (0, express_async_handler_1.default)(async (req, res) => {
        if (!req.lti) {
            throw new Error("No LTI session found");
        }
        const resource = await req.apiClient.getResource(req.lti.session.resource_uri);
        const studentUpdates = [];
        for (const key in req.body) {
            const match = key.match(/^student_uri_(.*)$/);
            if (match) {
                const student = match[1];
                const uri = req.body[key];
                const roles = Object.keys(req.body)
                    .filter((k) => k.startsWith(`student_${student}_`))
                    .map((k) => k.split("_")[2]);
                const devices = roles.map((r) => req.body[`student_${student}_${r}`]);
                const mapping = devices.map((d, i) => ({ role: roles[i], device: d }));
                console.log(uri, mapping);
                studentUpdates.push({ uri, data: { role_mapping: mapping } });
                //promises.push(req.apiClient.updateResourceStudent(uri, {role_mapping: mapping}))
            }
        }
        let students;
        if (studentUpdates.length > 0) {
            students = await req.apiClient.updateResourceStudents(resource.students_uri, studentUpdates);
        }
        else {
            students = await req.apiClient.listResourceStudents(resource.students_uri);
        }
        console.log(students);
        const template = await req.apiClient.getTemplate(resource.experiment_template_uri);
        const groupDevices = await Promise.all(template.configuration.devices.map((d) => req.apiClient.getDevice(d.device)));
        const roles = [];
        const selectable_devices = new Set();
        for (let i = 0; i < groupDevices.length; i++) {
            const device = groupDevices[i];
            if (device.type === "group") {
                roles.push({
                    role: template.configuration.devices[i].role,
                    devices: device.devices.map((d) => d.url),
                });
                device.devices.forEach((d) => selectable_devices.add(d.url));
            }
        }
        const devices = Object.fromEntries(await Promise.all(Array.from(selectable_devices).map(async (d) => [
            d,
            await req.apiClient.getDevice(d),
        ])));
        renderPage(req.path, language, res, req.user, {
            req,
            resource,
            students: students.map((s) => ({ ...s, role_mapping: Object.fromEntries(s.role_mapping.map((r) => [r.role, r.device])) })),
            roles,
            devices,
        });
    }));
    return router;
}
exports.lti_router = lti_router;
//# sourceMappingURL=lti.js.map