"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
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.openWithFinder = openWithFinder;
exports.openWithCode = openWithCode;
exports.generateApptileConfig = generateApptileConfig;
exports.cloneApp = cloneApp;
exports.cloneAppEc2 = cloneAppEc2;
exports.createAppFolder = createAppFolder;
exports.listWorkspaces = listWorkspaces;
exports.getPluginBundle = getPluginBundle;
exports.compilePluginBundle = compilePluginBundle;
exports.getLocalBundles = getLocalBundles;
exports.getAppLocation = getAppLocation;
exports.ensureDefaultWorkspace = ensureDefaultWorkspace;
exports.getAppConfigsFromFS = getAppConfigsFromFS;
exports.getWebSDKBundleFromFS = getWebSDKBundleFromFS;
exports.getFileTree = getFileTree;
exports.manageSourceFileOrFolder = manageSourceFileOrFolder;
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const axios_1 = __importDefault(require("axios"));
const node_fs_1 = require("node:fs");
const promises_1 = require("node:fs/promises");
const js_sha256_1 = require("js-sha256");
const projectSetup_1 = require("./projectSetup");
const websocket_1 = require("../websocket");
const compiler_1 = require("./compiler");
const utils_1 = require("../utils");
const cliconfig_1 = require("../database/cliconfig");
const logger_1 = require("../utils/logger");
const fs = __importStar(require("fs/promises"));
const homeDir = os_1.default.homedir();
function openWithFinder(location) {
    return __awaiter(this, void 0, void 0, function* () {
        const command = `open -R ${location}`;
        console.log("Executing: " + command);
        yield (0, utils_1.exec)(command);
    });
}
function openWithCode(location) {
    return __awaiter(this, void 0, void 0, function* () {
        const command = `code ${location}`;
        console.log("Executing: " + command);
        yield (0, utils_1.exec)(command);
    });
}
function generateApptileConfig(existingConfig) {
    return __awaiter(this, void 0, void 0, function* () {
        const appId = existingConfig.APP_ID;
        const openedApp = yield (0, cliconfig_1.getOpenApp)(appId);
        try {
            if (openedApp) {
                // TODO(gaurav) replace this with actual api call
                let appconfig = { data: {} };
                if (process.env.NODE_ENV === "production") {
                    appconfig = (yield axios_1.default.get(`${existingConfig.APPTILE_BUILD_MANAGER_URL}/build-manager/api/build/${appId}/buildConfig`, {
                        headers: { "x-app-id": appId },
                    })).data;
                }
                else {
                    if (openedApp.apptilecookie) {
                        const headers = (0, utils_1.makeHeadersWithCookie)(openedApp.apptilecookie, {
                            "x-app-id": appId,
                        });
                        appconfig = (yield axios_1.default.get(`${existingConfig.APPTILE_BACKEND_URL}/build-system/api/build/${appId}/buildConfig`, headers)).data;
                    }
                    else {
                        console.error("Cookie not set");
                        return;
                    }
                }
                /*
                const appconfig = {"apptile_api_endpoint":"http://apptile-server:3000","analytics_api_endpoint":"https://staging-analytics.apptile.io","apptile_update_endpoint":"https://dev-appconfigs.apptile.io","app_id":"100d9050-7c76-4427-8221-ed3a3a1b4163","app_name":"test","url_scheme":"dsadassda","app_host":"daadsasaw","app_host_2":"dwadasdasadsads","build_ios":true,"build_android":true,"ios":{"icon_path":"100d9050-7c76-4427-8221-ed3a3a1b4163/icon/f766ec42-3518-44a2-bb0a-c8d3e76c32d9/icon.png","splash_path":"100d9050-7c76-4427-8221-ed3a3a1b4163/splash/52f30302-0df9-47a5-9cbf-4b12b8a86f27/splash.png","bundle_id":"com.apple.app","team_id":"8SQ493N52G"},"android":{"icon_path":"100d9050-7c76-4427-8221-ed3a3a1b4163/icon/f766ec42-3518-44a2-bb0a-c8d3e76c32d9/icon.png","splash_path":"100d9050-7c76-4427-8221-ed3a3a1b4163/splash/52f30302-0df9-47a5-9cbf-4b12b8a86f27/splash.png","bundle_id":"adasdasdsadasasd","service_file_path":"100d9050-7c76-4427-8221-ed3a3a1b4163/androidFirebaseServiceFile/28c75bcf-600d-4448-948b-78bcac5c2bc5/androidFirebaseServiceFile.json","expected_output":"apk&aab"},"both":{"enableOneSignal":true,"enableApptileAnalytics":false,"enableFBSDK":false,"enableSourceMap":false,"enableCleverTap":false,"enableKlaviyo":false,"enableMoEngage":false},"integrations":{"oneSignal":{"onesignal_app_id":"525896ec-34c0-4131-804c-79f8706ab6e4"},"metaAds":{"FacebookAppId":"570147808995354","FacebookClientToken":"5c95639f9583bc38a9fea5075b04b1cf","FacebookDisplayName":"Pilgrim Staging","FacebookAutoLogAppEventsEnabled":"YES","FacebookAdvertiserIDCollectionEnabled":"NO"},"appsflyer":{"devkey":"xxx","appId":"xxx"},"moengage":{"appId":"xxx","datacenter":"xxx"}},"assets":[{"fileName":"icon.png","assetClass":"icon","url":"https://apptile-demo-build-assets.s3.us-east-1.amazonaws.com/100d9050-7c76-4427-8221-ed3a3a1b4163/icon/f766ec42-3518-44a2-bb0a-c8d3e76c32d9/icon.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA2Q4O3KBMOLZE7QST%2F20250207%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250207T115237Z&X-Amz-Expires=3600&X-Amz-Signature=a389ec27158e62ca0812c204613d5d8f1e04ef3411b4f4b9f9b24cf6fe801eca&X-Amz-SignedHeaders=host&x-id=GetObject"},{"fileName":"splash.png","assetClass":"splash","url":"https://apptile-demo-build-assets.s3.us-east-1.amazonaws.com/100d9050-7c76-4427-8221-ed3a3a1b4163/splash/52f30302-0df9-47a5-9cbf-4b12b8a86f27/splash.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA2Q4O3KBMOLZE7QST%2F20250207%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250207T115237Z&X-Amz-Expires=3600&X-Amz-Signature=69f503f05715f1fa0b7d4c03e8b7fd9d33a293aa8ed1d4911833e355b524f2e8&X-Amz-SignedHeaders=host&x-id=GetObject"}],"secrets":[{"id":"185c6774-c5fb-454d-8a02-27083871f602","appId":"100d9050-7c76-4427-8221-ed3a3a1b4163","secretClass":"keyAlias","deletedAt":null,"createdAt":"2025-02-07T11:46:00.980Z","secret":"saddsa"},{"id":"bfbd6660-f7e2-42a0-8a13-fa3940f1b9da","appId":"100d9050-7c76-4427-8221-ed3a3a1b4163","secretClass":"storePassword","deletedAt":null,"createdAt":"2025-02-07T11:46:00.985Z","secret":"adssad"},{"id":"625823e4-54dc-40eb-ad6e-117ca2de84ed","appId":"100d9050-7c76-4427-8221-ed3a3a1b4163","secretClass":"keyPassword","deletedAt":null,"createdAt":"2025-02-07T11:46:00.987Z","secret":"adsdsa"}],"feature_flags":{"ENABLE_ONESIGNAL":true,"ENABLE_SEGMENT_ANALYTICS":false,"ENABLE_FBSDK":true,"ENABLE_CLEVERTAP":false,"ENABLE_KALVIYO":false,"ENABLE_MOENGAGE":true}};
                */
                const SDK_PATH = existingConfig.SDK_PATH;
                console.log("writing apptile.config.json to: ", openedApp.repoPath);
                yield (0, promises_1.writeFile)(path_1.default.resolve(openedApp.repoPath, "apptile.config.json"), JSON.stringify(Object.assign(Object.assign({ "-comment-": "This file is generated by cli. You can edit it to use different configs, but will be overwritten on regeneration." }, appconfig), { SDK_PATH: path_1.default.resolve(openedApp.repoPath, "../ReactNativeTSProjeect"), APP_ID: appId, APPTILE_BACKEND_URL: existingConfig.APPTILE_BACKEND_URL, APPCONFIG_SERVER_URL: existingConfig.APPCONFIG_SERVER_URL, APPTILE_BUILD_MANAGER_URL: existingConfig.APPTILE_BUILD_MANAGER_URL }), null, 2));
            }
            else {
                console.error("Cannot generate apptileconfig because either app is not open or cookie is not set!");
            }
        }
        catch (err) {
            console.error("Failed to generate apptileconfig: ", err);
        }
    });
}
function cloneApp(workspaceId, manifest, appIntegrations, infoFromApptileIO) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            (0, websocket_1.sendLog)("cloning app");
            const workspace = (0, cliconfig_1.getWorkspace)(workspaceId);
            if (workspace) {
                const entries = yield (0, promises_1.readdir)(path_1.default.resolve(workspace.location), {
                    withFileTypes: true,
                });
                for (let entry of entries) {
                    if (entry.isDirectory() && entry.name !== "ReactNativeTSProjeect") {
                        let apptileConfig = {};
                        try {
                            const apptileConfigRaw = yield (0, promises_1.readFile)(path_1.default.resolve(workspace.location, entry.name, "apptile.config.json"), { encoding: "utf8" });
                            apptileConfig = JSON.parse(apptileConfigRaw);
                        }
                        catch (err) {
                            console.error("Found a folder with invalid or nonexistent apptile.config.json", err);
                        }
                        const appId = apptileConfig.APP_ID;
                        if (manifest.uuid == appId) {
                            throw new Error(`clone already exists for ${appId} at ${path_1.default.resolve(workspace.location, entry.name)}`);
                        }
                    }
                }
                const targetName = (0, utils_1.createTargetName)(manifest.name, manifest.uuid);
                const appLocation = path_1.default.resolve(workspace.location, targetName);
                if (manifest.gitRepo) {
                    yield (0, utils_1.exec)(`git clone --depth=1 ${manifest.gitRepo} ${targetName}`, {
                        cwd: workspace.location,
                    });
                }
                else {
                    // unzip to location
                    yield (0, utils_1.exec)(`git clone --depth=1 git@github.com:clearsight-dev/apptile-seed.git ${targetName}`, { cwd: workspace.location });
                    const xcodeEnvLocation = path_1.default.resolve(workspace.location, targetName, "ios/.xcode.env");
                    let xcodeEnv = yield (0, promises_1.readFile)(xcodeEnvLocation, { encoding: "utf8" });
                    xcodeEnv = xcodeEnv.replace("# APPTILE_CLI_NODE_PATH_ENTRY", `export NODE_BINARY=${process.execPath}`);
                    yield (0, promises_1.writeFile)(xcodeEnvLocation, xcodeEnv);
                    // Update Info.plist
                    /*
                  const infoPlistLocation = path.resolve(appLocation, 'ios/apptileSeed/Info.plist');
                  const rawInfoPlist = await readFile(infoPlistLocation, {encoding: 'utf8'});
                  const infoPlist = plist.parse(rawInfoPlist) as any;
                  infoPlist.APPTILE_API_ENDPOINT = APPTILE_BACKEND_URL;
                  infoPlist.APPTILE_UPDATE_ENDPOINT = APPCONFIG_SERVER_URL;
                  const updatedPlist = plist.build(infoPlist);
                  await writeFile(infoPlistLocation, updatedPlist);
                  */
                }
                const SDK_PATH = path_1.default.resolve(workspace.location, "ReactNativeTSProjeect");
                const APP_ID = manifest.uuid;
                const APPCONFIG_SERVER_URL = infoFromApptileIO.appconfigServer;
                let APPTILE_BACKEND_URL = infoFromApptileIO.apptileServer;
                const nets = os_1.default.networkInterfaces();
                const localIpInterface = Object.values(nets)
                    .flatMap((it) => it)
                    .filter((it) => it.family === "IPv4" &&
                    it.address !== "127.0.0.1" &&
                    it.netmask !== "255.255.0.0");
                if (localIpInterface.length === 0) {
                    console.error("Could not determine local ip address");
                }
                else {
                    const ip = localIpInterface[0].address;
                    if (APPTILE_BACKEND_URL.includes("api.apptile.local")) {
                        APPTILE_BACKEND_URL = `http://${ip}:3001`;
                    }
                }
                console.log("Writing apptile.config.json");
                yield (0, promises_1.writeFile)(path_1.default.resolve(appLocation, "apptile.config.json"), JSON.stringify({
                    "-comment-": "Partial config. Set apptilecookie and hit generate appconfig in cli to get the rest.",
                    SDK_PATH: SDK_PATH,
                    APP_ID: APP_ID,
                    APPTILE_BACKEND_URL: APPTILE_BACKEND_URL,
                    APPCONFIG_SERVER_URL: APPCONFIG_SERVER_URL,
                }, null, 2));
                // if appintegrations exist that have repos then update package.json and codegen relevant plugins/navs
                console.log("Codegen integrations");
                if (appIntegrations.length > 0) {
                    yield (0, compiler_1.codegenIntegrations)(appLocation, appIntegrations);
                }
                const to = path_1.default.resolve(workspace.location, targetName);
                /*
              const appDelegatePath = path.resolve(to, 'ios/apptileSeed/AppDelegate.mm');
              let appDelegate = await readFile(
                appDelegatePath,
                {encoding: 'utf8'}
              );
              appDelegate = appDelegate.replace('__METRO_IP__', `http://${ip}:8081`);
              await writeFile(appDelegatePath, appDelegate)
              sendLog("Start installation");
              */
                console.log("Starting installation for Node packages and PODS");
                yield (0, projectSetup_1.npmInstall)(to);
                yield (0, projectSetup_1.podInstall)(to);
            }
            else {
                (0, websocket_1.sendLog)("Couldn't find the workspace to clone into");
                throw new Error("workspace doesn't exist");
            }
        }
        catch (error) {
            console.error("Failed to clone app: ", error);
        }
    });
}
function cloneAppEc2(workspaceId, manifest, appIntegrations, infoFromApptileIO) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            logger_1.logger.info("cloning app: " + JSON.stringify(manifest, null, 2));
            const workspace = (0, cliconfig_1.getWorkspace)(workspaceId);
            if (workspace) {
                const entries = yield (0, promises_1.readdir)(path_1.default.resolve(workspace.location), {
                    withFileTypes: true,
                });
                for (let entry of entries) {
                    if (entry.isDirectory() && entry.name !== "ReactNativeTSProjeect") {
                        let apptileConfig = {};
                        try {
                            const apptileConfigRaw = yield (0, promises_1.readFile)(path_1.default.resolve(workspace.location, entry.name, "apptile.config.json"), { encoding: "utf8" });
                            apptileConfig = JSON.parse(apptileConfigRaw);
                        }
                        catch (err) {
                            console.error("Found a folder with invalid or nonexistent apptile.config.json", err);
                        }
                        const appId = apptileConfig.APP_ID;
                        if (manifest.uuid == appId) {
                            throw new Error(`clone already exists for ${appId} at ${path_1.default.resolve(workspace.location, entry.name)}`);
                        }
                    }
                }
                const targetName = (0, utils_1.createTargetName)(manifest.name, manifest.uuid);
                const appLocation = path_1.default.resolve(workspace.location, targetName);
                if (manifest.gitRepo) {
                    yield (0, utils_1.exec)(`git clone --depth=1 ${manifest.gitRepo} ${targetName}`, {
                        cwd: workspace.location,
                    });
                }
                else {
                    // unzip to location
                    yield (0, utils_1.exec)(`git clone --depth=1 git@github.com:clearsight-dev/apptile-seed.git ${targetName}`, { cwd: workspace.location });
                }
                const SDK_PATH = path_1.default.resolve(workspace.location, "ReactNativeTSProjeect");
                const APP_ID = manifest.uuid;
                let APPCONFIG_SERVER_URL = "https://dev-appconfigs.apptile.io";
                let APPTILE_BACKEND_URL = infoFromApptileIO.apptileServer;
                let APPTILE_BUILD_MANAGER_URL;
                if (process.env.NODE_ENV === "production") {
                    APPTILE_BACKEND_URL = `${process.env.APPTILE_SERVER_PROTOCOL}://${process.env.APPTILE_SERVER_ENDPOINT}:${process.env.APPTILE_SERVER_PORT}`;
                    APPTILE_BUILD_MANAGER_URL = `${process.env.APPTILE_SERVER_PROTOCOL}://${process.env.APPTILE_SERVER_ENDPOINT}:${process.env.APPTILE_BUILD_MANAGER_PORT}`;
                    APPCONFIG_SERVER_URL = "https://appconfigs.apptile.io";
                }
                const to = path_1.default.resolve(workspace.location, targetName);
                console.log("Writing apptile.config.json");
                yield (0, promises_1.writeFile)(path_1.default.resolve(appLocation, "apptile.config.json"), JSON.stringify({
                    "-comment-": "Partial config. Set apptilecookie and hit generate appconfig in cli to get the rest.",
                    SDK_PATH: SDK_PATH,
                    APP_ID: APP_ID,
                    APPTILE_BACKEND_URL: APPTILE_BACKEND_URL,
                    APPCONFIG_SERVER_URL: APPCONFIG_SERVER_URL,
                    APPTILE_BUILD_MANAGER_URL: APPTILE_BUILD_MANAGER_URL,
                }, null, 2));
                // if appintegrations exist that have repos then update package.json and codegen relevant plugins/navs
                // logger.info("Codegen integrations");
                if (appIntegrations.length > 0) {
                    yield (0, compiler_1.codegenIntegrations)(appLocation, appIntegrations);
                }
                logger_1.logger.info("Starting installation for Node packages");
                yield (0, projectSetup_1.npmInstall)(to);
            }
            else {
                logger_1.logger.error("Couldn't find the workspace to clone info");
            }
        }
        catch (error) {
            logger_1.logger.error("Failed to clone app: ", error);
        }
    });
}
function createAppFolder(workspaceId, manifest, infoFromApptileIO) {
    return __awaiter(this, void 0, void 0, function* () {
        (0, websocket_1.sendLog)("Creating folder!");
        const workspace = (0, cliconfig_1.getWorkspace)(workspaceId);
        if (workspace) {
            const entries = yield (0, promises_1.readdir)(path_1.default.resolve(workspace.location), {
                withFileTypes: true,
            });
            for (let entry of entries) {
                if (entry.isDirectory() && entry.name !== "ReactNativeTSProjeect") {
                    let apptileConfig = {};
                    try {
                        const apptileConfigRaw = yield (0, promises_1.readFile)(path_1.default.resolve(workspace.location, entry.name, "apptile.config.json"), { encoding: "utf8" });
                        apptileConfig = JSON.parse(apptileConfigRaw);
                    }
                    catch (err) {
                        console.error("Found a folder with invalid or nonexistent apptile.config.json", err);
                    }
                    const appId = apptileConfig.APP_ID;
                    if (manifest.uuid == appId) {
                        throw new Error(`clone already exists for ${appId} at ${path_1.default.resolve(workspace.location, entry.name)}`);
                    }
                }
            }
            const targetName = (0, utils_1.createTargetName)(manifest.name, manifest.uuid);
            const appLocation = path_1.default.resolve(workspace.location, targetName);
            if (manifest.gitRepo) {
                yield (0, utils_1.exec)(`git clone --depth=1 ${manifest.gitRepo} ${targetName}`, {
                    cwd: workspace.location,
                });
            }
            else {
                // unzip to location
                yield (0, utils_1.exec)(`git clone --depth=1 git@github.com:clearsight-dev/apptile-seed.git ${targetName}`, { cwd: workspace.location });
            }
            console.log("Folder created: ", targetName);
            const SDK_PATH = path_1.default.resolve(workspace.location, "ReactNativeTSProjeect");
            const APP_ID = manifest.uuid;
            let APPCONFIG_SERVER_URL = "https://dev-appconfigs.apptile.io";
            let APPTILE_BACKEND_URL = infoFromApptileIO.apptileServer;
            let APPTILE_BUILD_MANAGER_URL;
            if (process.env.NODE_ENV === "production") {
                APPTILE_BACKEND_URL = `${process.env.APPTILE_SERVER_PROTOCOL}://${process.env.APPTILE_SERVER_ENDPOINT}:${process.env.APPTILE_SERVER_PORT}`;
                APPTILE_BUILD_MANAGER_URL = `${process.env.APPTILE_SERVER_PROTOCOL}://${process.env.APPTILE_SERVER_ENDPOINT}:${process.env.APPTILE_BUILD_MANAGER_PORT}`;
                APPCONFIG_SERVER_URL = "https://appconfigs.apptile.io";
            }
            yield (0, promises_1.writeFile)(path_1.default.resolve(appLocation, "apptile.config.json"), JSON.stringify({
                "-comment-": "Partial config. Set apptilecookie and hit generate appconfig in cli to get the rest.",
                SDK_PATH: SDK_PATH,
                APP_ID: APP_ID,
                APPTILE_BACKEND_URL: APPTILE_BACKEND_URL,
                APPCONFIG_SERVER_URL: APPCONFIG_SERVER_URL,
                APPTILE_BUILD_MANAGER_URL: APPTILE_BUILD_MANAGER_URL,
            }, null, 2));
            console.log("Apptile config file written");
        }
    });
}
function listWorkspaces() {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const workspaces = (0, cliconfig_1.getWorkspaces)();
            if (workspaces) {
                let result = [];
                for (let workspace of workspaces) {
                    const sdkLocation = path_1.default.resolve(workspace.location, "ReactNativeTSProjeect");
                    let { stdout: branchName } = yield (0, utils_1.exec)(`git rev-parse --abbrev-ref HEAD`, { cwd: sdkLocation });
                    branchName = branchName.trim();
                    // let {stdout: sdkHash} = await exec(`git log -1 --format="%H"`, {cwd: sdkLocation})
                    // sdkHash = sdkHash.trim();
                    // let { stdout: sdkHash } = await exec(
                    //   `git rev-list --left-right --count HEAD...origin/${branchName}`,
                    //   { cwd: sdkLocation }
                    // );
                    // sdkHash = sdkHash.trim();
                    const sdkHash = "none";
                    let workspaceResult = {
                        id: workspace.id,
                        sdkHash: `${branchName} (${sdkHash})`,
                        location: workspace.location,
                        repos: [],
                    };
                    const entries = yield (0, promises_1.readdir)(path_1.default.resolve(workspace.location), {
                        withFileTypes: true,
                    });
                    for (let entry of entries) {
                        const isDir = entry.isDirectory();
                        if (isDir && entry.name !== "ReactNativeTSProjeect") {
                            const repo = {
                                name: entry.name,
                                fullPath: path_1.default.resolve(workspace.location, entry.name),
                                gitRepo: "",
                                appId: "",
                                apptileServer: "",
                                appconfigServer: "",
                                isOpen: false,
                                codePushBundles: {
                                    ios: [],
                                    android: [],
                                },
                            };
                            // get appid and git branch
                            const apptileConfigPath = path_1.default.resolve(workspace.location, entry.name, "apptile.config.json");
                            try {
                                const rawConfig = yield (0, promises_1.readFile)(apptileConfigPath, {
                                    encoding: "utf-8",
                                });
                                const config = JSON.parse(rawConfig);
                                repo.appId = config.APP_ID;
                                repo.apptileServer = config.APPTILE_BACKEND_URL;
                                repo.appconfigServer = config.APPCONFIG_SERVER_URL;
                                repo.isOpen = !!(yield (0, cliconfig_1.getOpenApp)(config.APP_ID));
                            }
                            catch (err) {
                                console.error("Failed to get appid from repo: ", err);
                                repo.appId = "error";
                            }
                            try {
                                const { stdout, stderr } = yield (0, utils_1.exec)(`git remote get-url origin`, { cwd: path_1.default.resolve(workspace.location, entry.name) });
                                if (stdout) {
                                    repo.gitRepo = stdout.trim();
                                }
                                else {
                                    repo.gitRepo = stderr.trim();
                                }
                            }
                            catch (err) {
                                console.error("Failed to get git repository origin ", err);
                            }
                            // get ios and android folder contents for codePushBundles
                            try {
                                const bundles = yield (0, promises_1.readdir)(path_1.default.resolve(workspace.location, entry.name, "remoteCode/generated/bundles/ios"), { withFileTypes: true });
                                for (let bundle of bundles) {
                                    if (bundle.isDirectory()) {
                                        repo.codePushBundles.ios.push({
                                            fullPath: path_1.default.resolve(bundle.parentPath, bundle.name),
                                            timestamp: parseInt(bundle.name),
                                        });
                                    }
                                }
                            }
                            catch (err) {
                                // console.error("Failed to get ios codepush bundles", err);
                            }
                            try {
                                const bundles = yield (0, promises_1.readdir)(path_1.default.resolve(workspace.location, entry.name, "remoteCode/generated/bundles/android"), { withFileTypes: true });
                                for (let bundle of bundles) {
                                    if (bundle.isDirectory()) {
                                        repo.codePushBundles.android.push({
                                            fullPath: path_1.default.resolve(bundle.parentPath, bundle.name),
                                            timestamp: parseInt(bundle.name),
                                        });
                                    }
                                }
                            }
                            catch (err) {
                                // console.error("Failed to get android codepush bundles", err);
                            }
                            workspaceResult.repos.push(repo);
                        }
                    }
                    result.push(workspaceResult);
                }
                return result;
            }
            else {
                throw new Error("Failed when getting workspaces");
            }
        }
        catch (err) {
            console.error("Failed to get workspaces: ", err);
            throw err;
        }
    });
}
function getPluginBundle(appId) {
    return __awaiter(this, void 0, void 0, function* () {
        // Get opened app. If its not open return error
        const openedApp = yield (0, cliconfig_1.getOpenApp)(appId);
        if (!openedApp) {
            throw new Error("Open the app first.");
        }
        else {
            // Get location from opened app
            const appLocation = openedApp.repoPath;
            // list all plugins from the folder
            // get hash for plugins
            const remoteCode = path_1.default.resolve(appLocation, 'remoteCode');
            const pluginsRoot = path_1.default.resolve(remoteCode, 'plugins');
            const plugins = [];
            try {
                const pluginEntries = yield (0, promises_1.readdir)(pluginsRoot, { withFileTypes: true });
                for (let entry of pluginEntries) {
                    if (entry.isDirectory()) {
                        plugins.push(entry.name);
                    }
                }
            }
            catch (err) {
                if ((err === null || err === void 0 ? void 0 : err.code) !== 'ENOENT') {
                    console.error("Unhandelable error", err);
                    throw err;
                }
            }
            const generatedRoot = path_1.default.resolve(remoteCode, 'generated');
            const generatedFolderName = (0, js_sha256_1.sha256)(plugins.join('_')).toString();
            let bundlePath = path_1.default.resolve(generatedRoot, generatedFolderName, 'dist/bundle.js');
            try {
                yield (0, promises_1.stat)(bundlePath);
            }
            catch (err) {
                if ((err === null || err === void 0 ? void 0 : err.code) === 'ENOENT') {
                    const compileResult = yield (0, compiler_1.compileMultiplePlugins)(remoteCode, plugins, generatedFolderName);
                    if (!compileResult.errors) {
                        bundlePath = path_1.default.resolve(generatedRoot, 'dist/bundle.js');
                    }
                    else {
                        console.error('COMPILATION FAILED: ', compileResult);
                        throw new Error(compileResult.message);
                    }
                }
                else {
                    throw err;
                }
            }
            return bundlePath;
        }
    });
}
function compilePluginBundle(appId) {
    return __awaiter(this, void 0, void 0, function* () {
        const openedApp = yield (0, cliconfig_1.getOpenApp)(appId);
        if (!openedApp) {
            throw new Error("Open the app first.");
        }
        else {
            const appLocation = openedApp.repoPath;
            const remoteCode = path_1.default.resolve(appLocation, 'remoteCode');
            const pluginsRoot = path_1.default.resolve(remoteCode, 'plugins');
            const pluginEntries = yield (0, promises_1.readdir)(pluginsRoot, { withFileTypes: true });
            const plugins = [];
            for (let entry of pluginEntries) {
                if (entry.isDirectory()) {
                    plugins.push(entry.name);
                }
            }
            const generatedFolderName = (0, js_sha256_1.sha256)(plugins.join('_')).toString();
            const result = yield (0, compiler_1.compileMultiplePlugins)(remoteCode, plugins, generatedFolderName);
            return result;
        }
    });
}
function getLocalBundles(appLocation) {
    return __awaiter(this, void 0, void 0, function* () {
        const iosBundlesFolder = path_1.default.resolve(appLocation, 'remoteCode/generated/bundles/ios');
        let iosBundles = [];
        const androidBundlesFolder = path_1.default.resolve(appLocation, 'remoteCode/generated/bundles/android');
        let androidBundles = [];
        try {
            const entries = yield (0, promises_1.readdir)(iosBundlesFolder, { withFileTypes: true });
            for (let entry of entries) {
                iosBundles.push({
                    fullPath: path_1.default.resolve(iosBundlesFolder, entry.name, 'bundle.zip'),
                    timestamp: parseInt(entry.name)
                });
            }
        }
        catch (err) {
            console.error("Failed to get local ios bundles: ", err);
        }
        try {
            const entries = yield (0, promises_1.readdir)(androidBundlesFolder, { withFileTypes: true });
            for (let entry of entries) {
                androidBundles.push({
                    fullPath: path_1.default.resolve(androidBundlesFolder, entry.name, 'bundle.zip'),
                    timestamp: parseInt(entry.name)
                });
            }
        }
        catch (err) {
            console.error("Failed to get local ios bundles: ", err);
        }
        return {
            ios: iosBundles,
            android: androidBundles
        };
    });
}
function getAppLocation(workspaceId, appId) {
    return __awaiter(this, void 0, void 0, function* () {
        const workspace = (0, cliconfig_1.getWorkspace)(workspaceId);
        if (workspace) {
            const entries = yield (0, promises_1.readdir)(workspace.location, { withFileTypes: true });
            let appLocation = '';
            for (let entry of entries) {
                if (!['ReactNativeTSProjeect', '.DS_Store'].includes(entry.name)) {
                    try {
                        const apptileConfigPath = path_1.default.resolve(workspace.location, entry.name, 'apptile.config.json');
                        const rawConfig = yield (0, promises_1.readFile)(apptileConfigPath, { encoding: 'utf8' });
                        const config = JSON.parse(rawConfig);
                        if (appId === config.APP_ID) {
                            appLocation = path_1.default.resolve(workspace.location, entry.name);
                            break;
                        }
                    }
                    catch (err) {
                        console.error("Failed to read apptile config", err);
                    }
                }
            }
            return appLocation;
        }
        else {
            console.error("Could not find workspace");
            throw new Error("Not found");
        }
    });
}
function ensureDefaultWorkspace() {
    return __awaiter(this, void 0, void 0, function* () {
        console.log("ensuring default workspace");
        // ensure that workspaces table has an entry linking path to ~/apptile-cli-home
        const defaultWorkspaceLocation = path_1.default.resolve(homeDir, 'apptile-cli-home');
        const workspace = (0, cliconfig_1.getWorkspaceByLocation)(defaultWorkspaceLocation);
        if (!workspace) {
            console.log("Creating database entry");
            (0, cliconfig_1.createWorkspace)(defaultWorkspaceLocation);
        }
        let create = false;
        try {
            yield (0, promises_1.readdir)(defaultWorkspaceLocation);
        }
        catch (err) {
            if ((err === null || err === void 0 ? void 0 : err.code) === 'ENOENT') {
                console.log("default cli home folder doesn't exist");
                create = true;
            }
            else {
                console.error("Failed to verify existence of default workspace", err);
                throw err;
            }
        }
        let clone = false;
        const defaultSDKLocation = path_1.default.resolve(defaultWorkspaceLocation, 'ReactNativeTSProjeect');
        if (!create) {
            try {
                yield (0, promises_1.stat)(defaultSDKLocation);
            }
            catch (err) {
                if ((err === null || err === void 0 ? void 0 : err.code) === 'ENOENT') {
                    console.log("Default SDK clone doesn't exist");
                    clone = true;
                }
                else {
                    console.error("Failed to verify existence of sdk");
                    throw err;
                }
            }
        }
        const expectedRemote = 'git@github.com:clearsight-dev/ReactNativeTSProjeect.git';
        let remoteLocation = '';
        if (create) {
            console.log("Creating ~/apptile-cli-home");
            yield (0, promises_1.mkdir)(defaultWorkspaceLocation);
            console.log("Cloning ReactNativeTSProjeect SDK");
            const { stdout, stderr } = yield (0, utils_1.exec)(`git clone --depth=1 --branch=scion/v3-roadmap ${expectedRemote}`, { cwd: defaultWorkspaceLocation });
            remoteLocation = expectedRemote;
            console.log(stdout);
            console.log(stderr);
        }
        else if (clone) {
            console.log("Cloning SDK into existing ~/apptile-cli-home folder");
            const { stdout, stderr } = yield (0, utils_1.exec)(`git clone --depth=1 --branch=scion/v3-roadmap ${expectedRemote}`, { cwd: defaultWorkspaceLocation });
            remoteLocation = expectedRemote;
            console.log(stdout);
            console.log(stderr);
        }
        else {
            console.log("Checking validity of sdk in default workspace at: ", defaultSDKLocation);
            const { stdout, stderr } = yield (0, utils_1.exec)(`git remote get-url origin`, { cwd: defaultSDKLocation });
            remoteLocation = stdout.trim();
        }
        if (remoteLocation !== expectedRemote) {
            throw new Error(`workspace is corrupted. Expceted sdk to be cloned from ${expectedRemote} but found ${remoteLocation}`);
        }
        return `Paste the following in your browser console to get started: 
localStorage.setItem('plugin-server-url', 'http://localhost:3100/plugin-server'); localStorage.setItem('enablevim', 'yes'); window.location.reload();
`;
    });
}
function narrowProjectConfig(data) {
    return ((typeof (data === null || data === void 0 ? void 0 : data.SDK_PATH) === "string") &&
        (typeof (data === null || data === void 0 ? void 0 : data.APP_ID) === "string") &&
        (typeof (data === null || data === void 0 ? void 0 : data.APPTILE_BACKEND_URL) === "string") &&
        (typeof (data === null || data === void 0 ? void 0 : data.APPCONFIG_SERVER_URL) === "string"));
}
function getAppConfigsFromFS(appId) {
    return __awaiter(this, void 0, void 0, function* () {
        const openedApp = yield (0, cliconfig_1.getOpenApp)(appId);
        if (openedApp) {
            const configPath = path_1.default.resolve(openedApp.repoPath, 'apptile.config.json');
            const rawConfig = yield (0, promises_1.readFile)(configPath, { encoding: 'utf8' });
            const config = JSON.parse(rawConfig);
            if (narrowProjectConfig(config)) {
                return config;
            }
            else {
                throw new Error("Could not validate the apptile.config.json file's type");
            }
        }
        else {
            throw new Error("Could not find opened app");
        }
    });
}
function getWebSDKBundleFromFS() {
    return __awaiter(this, void 0, void 0, function* () {
        const webSDKBundle = (0, node_fs_1.createReadStream)(path_1.default.resolve(__dirname, '../dist/web-sdk-bundle.js'), 'utf-8');
        return webSDKBundle;
    });
}
// Helper function to recursively build the file tree with paths relative to a basePath
function _getFileTreeRecursive(currentDirPath, basePath) {
    return __awaiter(this, void 0, void 0, function* () {
        const entries = yield fs.readdir(currentDirPath, { withFileTypes: true });
        const nodes = [];
        for (const entry of entries) {
            const fullEntryPath = path_1.default.join(currentDirPath, entry.name);
            // Calculate path relative to the initial basePath
            const relativePath = path_1.default.relative(basePath, fullEntryPath);
            const node = {
                name: entry.name,
                type: entry.isDirectory() ? 'folder' : 'file',
                path: relativePath, // Store relative path
            };
            if (entry.isDirectory()) {
                // Recursively call with the full path of the directory and the original basePath
                node.children = yield _getFileTreeRecursive(fullEntryPath, basePath);
            }
            nodes.push(node);
        }
        return nodes;
    });
}
function getFileTree(dirPath) {
    return __awaiter(this, void 0, void 0, function* () {
        return _getFileTreeRecursive(dirPath, dirPath);
    });
}
function manageSourceFileOrFolder(params, sourceBasePath) {
    return __awaiter(this, void 0, void 0, function* () {
        const { itemType, itemName, relativePath, isFolder, action } = params;
        // Validate relativePath: must not be empty, '.', absolute, or contain '..'
        if (!relativePath || relativePath.trim() === '' || relativePath === '.') {
            logger_1.logger.warn(`manageSourceFileOrFolder: Invalid relativePath - empty or '.'`);
            throw new Error('Invalid relativePath: Must specify a file or folder name within the source directory.');
        }
        if (path_1.default.isAbsolute(relativePath)) {
            logger_1.logger.warn(`manageSourceFileOrFolder: Invalid relativePath - absolute path provided: ${relativePath}`);
            throw new Error('Invalid relativePath: Must not be an absolute path.');
        }
        const normalizedRelativePath = path_1.default.normalize(relativePath);
        // Disallow any '..' segments in the path for security.
        if (normalizedRelativePath.split(path_1.default.sep).includes('..') || normalizedRelativePath.startsWith('..' + path_1.default.sep) || normalizedRelativePath === '..') {
            logger_1.logger.warn(`manageSourceFileOrFolder: Invalid relativePath - contains '..': ${relativePath} (normalized: ${normalizedRelativePath})`);
            throw new Error('Invalid relativePath: ".." segments are not allowed, path traversal attempt suspected.');
        }
        const targetFullPath = path_1.default.join(sourceBasePath, normalizedRelativePath);
        // Security check: ensure the resolved targetPath is strictly within sourceBasePath
        if (!targetFullPath.startsWith(sourceBasePath + path_1.default.sep)) {
            logger_1.logger.error(`manageSourceFileOrFolder: Path integrity check failed. Resolved path: ${targetFullPath}. Base source path: ${sourceBasePath}`);
            throw new Error('Invalid path: Operation must be strictly within the designated source sub-directory.');
        }
        try {
            if (action === 'create') {
                if (isFolder) {
                    yield (0, promises_1.mkdir)(targetFullPath, { recursive: true });
                    const message = `Folder '${relativePath}' created successfully in ${itemType} '${itemName}'.`;
                    (0, websocket_1.sendLog)(message);
                    logger_1.logger.info(message + ` Path: ${targetFullPath}`);
                    return { success: true, message };
                }
                else {
                    const parentDir = path_1.default.dirname(targetFullPath);
                    yield (0, promises_1.mkdir)(parentDir, { recursive: true });
                    yield (0, promises_1.writeFile)(targetFullPath, '', { encoding: 'utf8' }); // Create an empty file
                    const message = `File '${relativePath}' created successfully in ${itemType} '${itemName}'.`;
                    (0, websocket_1.sendLog)(message);
                    logger_1.logger.info(message + ` Path: ${targetFullPath}`);
                    return { success: true, message };
                }
            }
            else if (action === 'delete') {
                let statsInfo;
                try {
                    statsInfo = yield (0, promises_1.stat)(targetFullPath);
                }
                catch (e) {
                    if (e.code === 'ENOENT') {
                        const message = `Cannot delete '${relativePath}' in ${itemType} '${itemName}': Path does not exist.`;
                        logger_1.logger.warn(message + ` Path: ${targetFullPath}`);
                        throw new Error(message);
                    }
                    logger_1.logger.error(`manageSourceFileOrFolder: Error stating path ${targetFullPath} before deletion: ${e.message}`);
                    throw e; // re-throw other stat errors
                }
                if (isFolder) {
                    if (!statsInfo.isDirectory()) {
                        const message = `Cannot delete '${relativePath}' in ${itemType} '${itemName}': Not a directory.`;
                        logger_1.logger.warn(message + ` Path: ${targetFullPath}`);
                        throw new Error(message);
                    }
                    yield (0, promises_1.rm)(targetFullPath, { recursive: true, force: true });
                    const message = `Folder '${relativePath}' deleted successfully from ${itemType} '${itemName}'.`;
                    (0, websocket_1.sendLog)(message);
                    logger_1.logger.info(message + ` Path: ${targetFullPath}`);
                    return { success: true, message };
                }
                else {
                    if (!statsInfo.isFile()) {
                        const message = `Cannot delete '${relativePath}' in ${itemType} '${itemName}': Not a file.`;
                        logger_1.logger.warn(message + ` Path: ${targetFullPath}`);
                        throw new Error(message);
                    }
                    yield (0, promises_1.unlink)(targetFullPath);
                    const message = `File '${relativePath}' deleted successfully from ${itemType} '${itemName}'.`;
                    (0, websocket_1.sendLog)(message);
                    logger_1.logger.info(message + ` Path: ${targetFullPath}`);
                    return { success: true, message };
                }
            }
            else {
                const message = `Invalid action: ${action}. Must be 'create' or 'delete'.`;
                logger_1.logger.warn(`manageSourceFileOrFolder: ${message}`);
                throw new Error(message);
            }
        }
        catch (error) {
            const errorMessage = `Operation failed for ${itemType} '${itemName}', path '${relativePath}', action '${action}'. Error: ${error.message}`;
            logger_1.logger.error(`manageSourceFileOrFolder: ${errorMessage}`, error);
            throw error;
        }
    });
}
