import _ from "lodash";
import turboCIPkgrabDirNames from "./turboci-pkg-grab-dir-names";
import { AppData } from "../data/app-data";

type Params = {
    private_server_ips: string[];
    script: string;
    work_dir?: string;
    parrallel?: boolean;
    no_process_logs?: boolean;
};

export default function bunGrabPrivateIPsBulkScripts({
    private_server_ips,
    script,
    work_dir,
    parrallel,
    no_process_logs,
}: Params) {
    const { relayServerSshPrivateKeyFile } = turboCIPkgrabDirNames();

    let bunCmd = "";

    bunCmd += `import _ from "lodash";\n`;
    bunCmd += `import { execSync } from "child_process";\n`;
    bunCmd += `\n`;
    bunCmd += `const SSH_KEY = "${relayServerSshPrivateKeyFile}";\n`;
    bunCmd += `const SSH_OPTS = \`-i \${SSH_KEY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -C -c aes128-ctr\`;\n`;
    bunCmd += `const TIMEOUT = ${AppData["ssh_try_timeout_milliseconds"]};\n`;
    bunCmd += `const MAX_ATTEMPTS = ${AppData["ssh_max_tries"]};\n`;

    bunCmd += `const REMOTE_HOSTS = [${private_server_ips
        .map((h) => `"${h.replace(/\"/g, "")}"`)
        .join(", ")}];\n`;

    bunCmd += `const DEFAULT_SSH_USER = "root";\n`;
    bunCmd += `const BATCH_SIZE = ${AppData["private_server_batch_exec_size"]};\n`;

    bunCmd += `async function run(host: string) {\n`;
    bunCmd += `    let attempt = 0;\n`;
    if (!no_process_logs) {
        bunCmd += `    console.log(\`Setting up \${host} ...\`);\n`;
    }
    bunCmd += `    while (attempt < MAX_ATTEMPTS) {\n`;
    bunCmd += `        attempt += 1;\n`;
    bunCmd += `        if (attempt > MAX_ATTEMPTS) {\n`;
    if (!no_process_logs) {
        bunCmd += `            console.log(\`Error: \${host} not ready after \${MAX_ATTEMPTS} attempts. Exiting.\`);\n`;
    }
    bunCmd += `            process.exit(1);\n`;
    bunCmd += `        }\n`;
    bunCmd += `        try {\n`;
    if (!no_process_logs) {
        bunCmd += `            console.log(\`Waiting for \${host} to be ready... (attempt \${attempt}/\${MAX_ATTEMPTS})\`);\n`;
    }
    bunCmd += `            let testCmd = \`ssh \${SSH_OPTS} \${DEFAULT_SSH_USER}@\${host} echo "Running ..."\`;\n`;
    bunCmd += `            let testRes = execSync(testCmd, { encoding: "utf-8" });\n`;
    bunCmd += `            if (testRes?.match(/Running/)) break;\n`;
    bunCmd += `        } catch (error) {\n`;
    if (!no_process_logs) {
        bunCmd += `            console.log(\`Attempt \${attempt} failed!\`);\n`;
    }
    bunCmd += `        }\n`;
    bunCmd += `        await Bun.sleep(TIMEOUT);\n`;
    bunCmd += `    }\n`;
    bunCmd += `    attempt = 0;\n`;
    bunCmd += `\n`;

    bunCmd += `    let execCmd = \`ssh \${SSH_OPTS} \${DEFAULT_SSH_USER}@\${host} << 'TURBOCIEXEC'\\n\`;\n`;

    if (work_dir) {
        bunCmd += `    execCmd += \`cd ${work_dir}\\n\`;\n`;
    }

    bunCmd += `    execCmd += \`${script}\\n\`;\n`;
    bunCmd += `    execCmd += \`TURBOCIEXEC\\n\`;\n`;
    bunCmd += `    try {\n`;
    bunCmd += `        const res = execSync(execCmd, { encoding: "utf-8" });\n`;
    bunCmd += `        console.log(res);\n`;
    bunCmd += `    } catch (error) {\n`;
    bunCmd += `        process.exit(1);\n`;
    bunCmd += `    }\n`;
    bunCmd += `}\n`;
    bunCmd += `\n`;

    if (parrallel) {
        bunCmd += `const first_host = REMOTE_HOSTS.splice(0,1)[0];\n`;
        bunCmd += `await run(first_host)\n`;
        bunCmd += `\n`;
        bunCmd += `const chunks = _.chunk(REMOTE_HOSTS, BATCH_SIZE);\n`;
        bunCmd += `for (let i = 0; i < chunks.length; i++) {\n`;
        bunCmd += `    const chunk = chunks[i];\n`;
        bunCmd += `    const runChunk = await Promise.all(chunk.map(h => run(h)));\n`;
        bunCmd += `}\n`;
    } else {
        bunCmd += `for (let i = 0; i < REMOTE_HOSTS.length; i++) {\n`;
        bunCmd += `    const host = REMOTE_HOSTS[i];\n`;
        bunCmd += `    const runHost = await run(host);\n`;
        bunCmd += `}\n`;
    }

    return bunCmd;
}
