"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.jsonParser = void 0;
const express_1 = __importDefault(require("express"));
const projectSetup_1 = require("../controllers/projectSetup");
const body_parser_1 = __importDefault(require("body-parser"));
const projectSetup_2 = require("../controllers/projectSetup");
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const promises_1 = require("node:fs/promises");
const form_data_1 = __importDefault(require("form-data"));
const axios_1 = __importDefault(require("axios"));
const websocket_1 = require("../websocket");
const compiler_1 = require("../controllers/compiler");
const utils_1 = require("../utils");
const cliconfig_1 = require("../database/cliconfig");
const workspace_1 = require("../controllers/workspace");
exports.jsonParser = body_parser_1.default.json({ limit: '50mb' });
const router = express_1.default.Router();
router.get(`/devices`, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    try {
        const devices = yield (0, projectSetup_2.listDevices)();
        res.json(devices);
    }
    catch (err) {
        console.error("Failed to get devices");
        next(err);
    }
}));
router.post(`/:appid/bundles/:bundleid`, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    const appId = req.params.appid;
    const bundleId = req.params.bundleid;
    try {
        const apptileConfig = yield (0, workspace_1.getAppConfigsFromFS)(appId);
        yield axios_1.default.put(`${apptileConfig.APPTILE_BACKEND_URL}/api/v2/app/${appId}/deploy/${bundleId}`);
        res.status(200);
        res.end();
    }
    catch (err) {
        console.error("Failed to publish", err);
        next(err);
    }
}));
router.post(`/:appId/runAndroid/:deviceId`, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    const appId = req.params.appId;
    const deviceId = req.params.deviceId;
    try {
        (0, projectSetup_1.runAndroid)(appId, deviceId);
        res.status(201);
        res.end();
    }
    catch (err) {
        next(err);
    }
}));
router.post(`/:appId/runIOS/:deviceId`, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    const appId = req.params.appId;
    const deviceId = req.params.deviceId;
    try {
        (0, projectSetup_1.runIOS)(appId, deviceId);
        res.status(201);
        res.end();
    }
    catch (err) {
        next(err);
    }
}));
router.get(`/:appid/bundles`, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
    const appId = req.params.appid;
    const openedApp = (0, cliconfig_1.getOpenApp)(appId);
    try {
        const apptileConfig = yield (0, workspace_1.getAppConfigsFromFS)(appId);
        const { data: manifest } = yield axios_1.default.get(`${apptileConfig.APPTILE_BACKEND_URL}/api/v2/app/${appId}/manifest`);
        const { data: bundles } = yield axios_1.default.get(`${apptileConfig.APPTILE_BACKEND_URL}/api/v2/app/${appId}/bundles`);
        let iosBundles = [];
        try {
            if (openedApp) {
                const entries = yield (0, promises_1.readdir)(path_1.default.resolve(openedApp.repoPath, 'remoteCode/generated/bundles/ios'), { withFileTypes: true });
                const formatter = Intl.DateTimeFormat('en-US', { timeStyle: 'short', dateStyle: 'full' });
                for (let entry of entries) {
                    if (entry.isDirectory()) {
                        try {
                            const name = new Date(parseInt(entry.name));
                            iosBundles.push({
                                name: entry.name,
                                date: formatter.format(name),
                                path: path_1.default.resolve(openedApp.repoPath, 'remoteCode/generated/bundles/ios', entry.name)
                            });
                        }
                        catch (err) { }
                    }
                }
            }
        }
        catch (err) {
            (0, websocket_1.sendLog)("Error in trying to get local ios bundles: " + (err === null || err === void 0 ? void 0 : err.message));
        }
        if (openedApp) {
            res.json({ openedApp, manifest, bundles, iosBundles });
        }
        else {
            res.status(404);
            res.end();
        }
    }
    catch (err) {
        res.render('error', { message: err === null || err === void 0 ? void 0 : err.message });
    }
}));
router.post('/:appid/build/:os/:mode', (req, res) => {
    const appId = req.params.appid;
    const os = req.params.os;
    const mode = req.params.mode;
    if ((0, projectSetup_1.narrowOSName)(os) && (0, projectSetup_1.narrowBuildMode)(mode)) {
        const deviceId = req.query.deviceId;
        (0, projectSetup_1.buildApp)(appId, os, mode, deviceId);
        res.status(201);
        res.end();
    }
    else {
        console.error("Wrong os name or build mode");
        res.status(400);
        res.end();
    }
});
router.delete('/killmetro', (req, res) => {
    (0, projectSetup_1.killMetro)();
    res.status(200);
    res.end();
});
router.get('/close-install-popup', (req, res) => {
    res.render('installPopup.pug', { visible: false, valid: false });
});
router.post("/:appid/uploadMobileBundle/:bundleName/:os", (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    try {
        const appId = req.params.appid;
        const bundleName = req.params.bundleName;
        const os = req.params.os;
        const apptileConfig = yield (0, workspace_1.getAppConfigsFromFS)(appId);
        if ((0, projectSetup_1.narrowOSName)(os)) {
            const { err: bundleErr, assetZip } = yield (0, projectSetup_1.getMobileBundle)(appId, bundleName, os);
            const { err, gitsha, sdksha } = yield (0, projectSetup_1.getGitShas)(appId);
            if (err || bundleErr) {
                console.error("Failed to retrieve bundle", err, bundleErr);
                res.status(500);
                res.end();
            }
            else if (assetZip) {
                let formData = new form_data_1.default();
                formData.append('assetZipFile', (0, fs_1.createReadStream)(assetZip));
                formData.append('uploadDestination', os === 'ios' ? 'ios-jsbundle' : 'android-jsbundle');
                formData.append('gitsha', gitsha);
                formData.append('sdksha', sdksha);
                formData.append('tag', 'sometag');
                let res = yield axios_1.default.post(`${apptileConfig.APPTILE_BACKEND_URL}/api/v2/app/${appId}/upload`, formData, {
                    headers: Object.assign({}, formData.getHeaders())
                });
                if (res.status >= 400) {
                    try {
                        const erroMessage = res.data;
                        throw new Error("Failed to upload " + erroMessage);
                    }
                    catch (err) {
                        console.error("Unprocessable error: ", err);
                        throw err;
                    }
                }
            }
            else {
                console.error("Link not found");
                next(new Error("Link not found when uploading mobile bundle"));
            }
        }
        else {
            next(new Error("Cannot upload bundle for os name: " + os));
        }
        res.status(200);
        res.end();
    }
    catch (err) {
        console.error("in uploadMobileBundle", err);
        next(err);
    }
}));
router.post("/:appid/refreshIntegrations", exports.jsonParser, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    // TODO(gaurav): this logic should be in a controller and the table 
    // should have new columns for all the columns that are being misused here
    const appId = req.params.appid;
    const openedApp = (0, cliconfig_1.getOpenApp)(appId);
    if (openedApp) {
        const integrations = req.body.integrations;
        (0, websocket_1.sendLog)("Refreshing integrations");
        try {
            // Check if a sdk integration has platformtype of apptilesdk
            let sdkIntegrations = [];
            for (let i = 0; i < integrations.length; ++i) {
                const integration = integrations[i];
                if (integration.packageLocation) {
                    sdkIntegrations.push(integration);
                }
            }
            const packageJsonNewEntries = {};
            // Codegen the relevant files
            for (let i = 0; i < sdkIntegrations.length; ++i) {
                const packageName = sdkIntegrations[i].integrationCode;
                const camelCasePackageName = (0, utils_1.toCamelCase)(packageName);
                const gitRepo = sdkIntegrations[i].packageLocation;
                (0, websocket_1.sendLog)(`Integration: ${packageName}: ${gitRepo}`);
                packageJsonNewEntries[packageName] = gitRepo;
                yield (0, promises_1.mkdir)(path_1.default.resolve(openedApp.repoPath, 'remoteCode/plugins', packageName, 'source'), { recursive: true });
                const pluginsLinkingFile = path_1.default.resolve(openedApp.repoPath, 'remoteCode/plugins', packageName, 'source/widget.jsx');
                yield (0, promises_1.writeFile)(pluginsLinkingFile, `import ${camelCasePackageName} from "${packageName}";
export default ${camelCasePackageName};\n`);
            }
            const existingPackageJSON = yield (0, promises_1.readFile)(path_1.default.resolve(openedApp.repoPath, 'package.json'), { encoding: 'utf8' });
            const parsedPackageJSON = JSON.parse(existingPackageJSON);
            let pkgJSONUpdated = false;
            for (let pkg in packageJsonNewEntries) {
                if (parsedPackageJSON.dependencies[pkg] != packageJsonNewEntries[pkg]) {
                    parsedPackageJSON.dependencies[pkg] = packageJsonNewEntries[pkg];
                    pkgJSONUpdated = true;
                }
            }
            if (pkgJSONUpdated) {
                // write updated package.json
                const updatedPkgJson = JSON.stringify(parsedPackageJSON, null, 2);
                (0, websocket_1.sendLog)(`Updated package.json\n${updatedPkgJson}`);
                yield (0, promises_1.writeFile)(path_1.default.resolve(openedApp.repoPath, 'package.json'), updatedPkgJson);
                // run npm install
                yield (0, projectSetup_1.npmInstall)(openedApp.repoPath);
            }
            else {
                (0, websocket_1.sendLog)("No updates required to package.json");
            }
            res.status(201);
            res.end();
        }
        catch (err) {
            (0, websocket_1.sendLog)("Failed while refreshing integrations " + (err === null || err === void 0 ? void 0 : err.message));
            next(err);
        }
    }
    else {
        res.status(404);
        res.end();
    }
}));
router.post("/:appid/uploadWebBundle", (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    try {
        const appid = req.params.appid;
        const apptileConfig = yield (0, workspace_1.getAppConfigsFromFS)(appid);
        const { err: pluginErr, res: pluginsBundle } = yield (0, projectSetup_2.currentPluginsBundle)(appid);
        const { err: navErr, res: navigatorsBundle } = yield (0, projectSetup_2.currentNavigatorsBundle)(appid);
        if (pluginErr || navErr) {
            console.error(pluginErr, navErr);
            res.render('error', { message: 'Failed to get plugin or navigator bundle' });
        }
        else {
            const { err, gitsha, sdksha } = yield (0, projectSetup_1.getGitShas)(appid);
            if (err) {
                res.render('error', { message: 'Failed to get git shas' });
                return;
            }
            else if (gitsha && sdksha) {
                // Upload plugins bundle
                let formData = new form_data_1.default();
                formData.append('file', (0, fs_1.createReadStream)(pluginsBundle));
                formData.append('uploadDestination', 'plugins');
                formData.append('gitsha', gitsha);
                formData.append('sdksha', sdksha);
                formData.append('tag', 'sometag');
                let res = yield axios_1.default.post(`${apptileConfig.APPTILE_BACKEND_URL}/api/v2/app/${appid}/upload`, formData, {
                    headers: Object.assign({}, formData.getHeaders())
                });
                if (res.status >= 400) {
                    try {
                        const erroMessage = res.data;
                        throw new Error("Failed to upload " + erroMessage);
                    }
                    catch (err) {
                        console.error("Unprocessable error: ", err);
                        throw err;
                    }
                }
                // Upload navigator bundle
                formData = new form_data_1.default();
                formData.append('file', (0, fs_1.createReadStream)(navigatorsBundle));
                formData.append('uploadDestination', 'navigators');
                formData.append('gitsha', gitsha);
                formData.append('sdksha', sdksha);
                formData.append('tag', 'sometag');
                res = yield axios_1.default.post(`${apptileConfig.APPTILE_BACKEND_URL}/api/v2/app/${appid}/upload`, formData, {
                    headers: Object.assign({}, formData.getHeaders())
                });
                if (res.status >= 400) {
                    try {
                        const erroMessage = res.data;
                        throw new Error("Failed to upload " + erroMessage);
                    }
                    catch (err) {
                        console.error("Unprocessable error: ", err);
                        throw err;
                    }
                }
            }
            else {
                throw new Error("Invalid code path");
            }
            res.status(200);
            res.end();
            // render the bundles maybe
        }
    }
    catch (err) {
        console.error("in ubloadWebBundles", err);
        next(err);
    }
}));
router.post("/:appid/bundleJsIOS", (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    const appId = req.params.appid;
    const openedApp = (0, cliconfig_1.getOpenApp)(appId);
    if (openedApp) {
        try {
            yield (0, compiler_1.generatIOSBundle)(openedApp.repoPath);
        }
        catch (err) {
            next(err);
        }
    }
    else {
        res.status(404);
        res.end();
    }
}));
router.post("/:appid/bundleJsAndroid", (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    const appId = req.params.appid;
    const openedApp = (0, cliconfig_1.getOpenApp)(appId);
    if (openedApp) {
        try {
            yield (0, compiler_1.generatAndroidBundle)(openedApp.repoPath);
        }
        catch (err) {
            next(err);
        }
    }
    else {
        res.status(404);
        res.end();
    }
}));
function buildFileTree(name, fullPath, depth) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const entries = yield (0, promises_1.readdir)(fullPath, { withFileTypes: true });
            const contents = [];
            for (let i = 0; i < entries.length; ++i) {
                const entry = entries[i];
                if (!['.DS_Store', 'idb-applications'].includes(entry.name)) {
                    if (entry.isDirectory()) {
                        if (depth > 1) {
                            const subtree = yield buildFileTree(entry.name, path_1.default.resolve(fullPath, entry.name), depth - 1);
                            contents.push(subtree);
                        }
                        else {
                            contents.push({
                                name: entry.name,
                                fullPath: path_1.default.resolve(fullPath, entry.name),
                                type: 'directory',
                                contents: []
                            });
                        }
                    }
                    else {
                        contents.push({
                            name: entry.name,
                            fullPath,
                            type: 'file',
                            contents: []
                        });
                    }
                }
            }
            return {
                name,
                fullPath,
                type: 'directory',
                contents
            };
        }
        catch (err) {
            if (err.code === 'ENOTDIR') {
                return {
                    name,
                    fullPath,
                    type: 'file',
                    contents: []
                };
            }
            else {
                console.error("Failed to build tree rooted at: ", err);
                return {
                    name,
                    fullPath,
                    type: 'unknown',
                    contents: []
                };
            }
        }
    });
}
router.get("/explore/:path/:depth", (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
    try {
        const depth = parseInt(decodeURIComponent(req.params.depth));
        const pathToExplore = decodeURIComponent(req.params.path);
        const rootPath = path_1.default.resolve(pathToExplore);
        const tree = yield buildFileTree(pathToExplore, rootPath, depth);
        res.json(tree);
    }
    catch (err) {
        console.error("Failed to explore: ", err);
        next(err);
    }
}));
exports.default = router;
