"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.openWithFinder = openWithFinder;
exports.openWithCode = openWithCode;
exports.cloneApp = cloneApp;
exports.listWorkspaces = listWorkspaces;
exports.getPluginBundle = getPluginBundle;
exports.compilePluginBundle = compilePluginBundle;
exports.getLocalBundles = getLocalBundles;
exports.getAppLocation = getAppLocation;
exports.ensureDefaultWorkspace = ensureDefaultWorkspace;
exports.getAppConfigsFromFS = getAppConfigsFromFS;
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const websocket_1 = require("../websocket");
const cliconfig_1 = require("../database/cliconfig");
const promises_1 = require("node:fs/promises");
const utils_1 = require("../utils");
const compiler_1 = require("./compiler");
const js_sha256_1 = require("js-sha256");
const projectSetup_1 = require("./projectSetup");
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 cloneApp(workspaceId, manifest, appIntegrations, apptileConfig) {
    return __awaiter(this, void 0, void 0, function* () {
        (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.toKebabCase)(manifest.name);
            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 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 !== 1) {
                throw new Error("Could not determine local ip address");
            }
            const ip = localIpInterface[0].address;
            const SDK_PATH = path_1.default.resolve(workspace.location, 'ReactNativeTSProjeect');
            const APP_ID = manifest.uuid;
            const CLI_BASE_URL = `${ip}:3100/plugin-server`;
            const APPCONFIG_SERVER_URL = apptileConfig.appconfigServer;
            let APPTILE_BACKEND_URL = apptileConfig.apptileServer;
            if (APPTILE_BACKEND_URL.includes('api.apptile.local')) {
                APPTILE_BACKEND_URL = `http://${ip}:3001`;
            }
            yield (0, promises_1.writeFile)(path_1.default.resolve(appLocation, 'apptile.config.json'), JSON.stringify({
                "-comment-": "This file is generated by cli and will be overwritten",
                "SDK_PATH": SDK_PATH,
                "APP_ID": APP_ID,
                "CLI_BASE_URL": CLI_BASE_URL,
                "APPTILE_BACKEND_URL": APPTILE_BACKEND_URL,
                "APPCONFIG_SERVER_URL": APPCONFIG_SERVER_URL
            }, null, 2));
            yield (0, promises_1.writeFile)(path_1.default.resolve(appLocation, 'apptile.config.js'), `// This file is generated by cli and will be overwritten
export const SDK_PATH =  "${SDK_PATH}";
export const APP_ID = "${APP_ID}";
export const CLI_BASE_URL = "${CLI_BASE_URL}";
export const APPTILE_BACKEND_URL = "${APPTILE_BACKEND_URL}";
export const APPCONFIG_SERVER_URL = "${APPCONFIG_SERVER_URL}";
`);
            // if appintegrations exist that have repos then update package.json and codegen relevant plugins/navs
            if (appIntegrations.length > 0) {
                yield (0, compiler_1.codegenIntegrations)(appLocation, appIntegrations);
            }
            const to = path_1.default.resolve(workspace.location, targetName);
            const appDelegatePath = path_1.default.resolve(to, 'ios/apptileSeed/AppDelegate.mm');
            let appDelegate = yield (0, promises_1.readFile)(appDelegatePath, { encoding: 'utf8' });
            appDelegate = appDelegate.replace('__METRO_IP__', `http://${ip}:8081`);
            yield (0, promises_1.writeFile)(appDelegatePath, appDelegate);
            (0, websocket_1.sendLog)("Start installation");
            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");
        }
    });
}
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 } = yield (0, utils_1.exec)(`git rev-list --left-right --count HEAD...origin/${branchName}`, { cwd: sdkLocation });
                    sdkHash = sdkHash.trim();
                    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 = !!(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");
                            }
                            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 ios codepush bundles");
                            }
                            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 = (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 = (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* () {
        // await new Promise((resolve) => {
        //   setTimeout(() => resolve({}), 5000)}
        // ); debugger
        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.CLI_BASE_URL) === "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 = (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");
        }
    });
}
