import { EXPLOITS, DARKWEB_EXPLOITS } from "./constants.js";

/**
 * remove write log ✓
 * loop
 *  get all servers
 *  nuke if possible
 *      send scripts hack, grow weaken
 *  backdoor factions
 *  buy grow servers
 *  start helpers if not running include home //remove from hacknet
 *      grow if needed
 *      weaken if needed
 *      hack if no grow or weaken
 *  do simple hack
 *      check if money available
 *      check threads
 *      run hack 
 *      run grow to recover
 *      run weaken if security too high
 *  buy hack tools
 */

/** @param {import(".").NS } ns */
export async function main(ns) {
    ns.disableLog('ALL');
    const doc = eval('document');


    function terminalCommand(command) {
        const terminalInput = doc.getElementById("terminal-input");
        if (terminalInput != null && terminalInput != undefined) {
            const handler = Object.keys(terminalInput)[1];
            terminalInput.value = command;
            terminalInput[handler].onChange({ target: terminalInput });
            terminalInput[handler].onKeyDown({ key: 'Enter', preventDefault: () => null });
        }
    }

    function maxPortsToHack() {
        let maxPorts = 0;
        EXPLOITS.forEach(exploit => {
            if (ns.fileExists(exploit.name)) ++maxPorts;
        });
        // ns.tryWritePort(2, `Max ports for hacking ${maxPorts}`);
        return maxPorts;
    }
    function analyzeServer(server) {
        let requiredHacking = ns.getServerRequiredHackingLevel(server);
        let requiredPorts = ns.getServerNumPortsRequired(server);
        let currentHacking = ns.getHackingLevel();

        return {
            name: server,
            rootAccess: ns.hasRootAccess(server),
            requiredHacking: requiredHacking,
            requiredPorts: requiredPorts,
            nukeAvailable: (currentHacking >= requiredHacking && maxPortsToHack() >= requiredPorts),
            currentMoney: ns.getServerMoneyAvailable(server),
            maxMoney: ns.getServerMaxMoney(server),
            currentSecurity: ns.getServerSecurityLevel(server),
            minSecurity: ns.getServerMinSecurityLevel(server),
            RAM: ns.getServerMaxRam(server),
        };
    }

    function scanServer(currentServer, previousServer = currentServer, network = []) {
        let availableServers = ns.scan(currentServer);
        network.push(analyzeServer(currentServer));

        for (let i = 0; i < availableServers.length; ++i) {
            let nextServer = availableServers[i];

            if (nextServer != currentServer && nextServer != previousServer) {
                scanServer(nextServer, currentServer, network);
            }
        }
        return network;
    }

    // if new game, buy TOR router, start hack course
    if (ns.getHackingLevel() == 1) {
        let terminal;
        let city;
        let travel;
        for (const elem of doc.querySelectorAll('div.MuiDrawer-root.MuiDrawer-docked p')) {
            if (elem.textContent == 'Terminal') {
                terminal = elem;
            }
            if (elem.textContent == 'City') {
                city = elem;
            }
            if (elem.textContent == 'Travel') {
                travel = elem;
            }
        }

        city.click(); await ns.sleep(200);

        for (const alphaEnt of doc.querySelectorAll('div#root p')) {
            if (alphaEnt.textContent == '    o-----+---x----o 4        T [alpha ent.]     o-------o   /') {
                alphaEnt.querySelector('span').click(); await ns.sleep(200);
                break;
            }
        }

        for (const TOR of doc.querySelectorAll('div#root button')) {
            if (TOR.textContent == 'Purchase TOR router - $200.000k') {
                TOR.click(); await ns.sleep(200);
                break;
            }
        }

        travel.click(); await ns.sleep(200);

        for (const aevum of doc.querySelectorAll('div#root span')) {
            if (aevum.textContent == 'A') {
                aevum.click(); await ns.sleep(200);
                break;
            }
        }

        for (const travelConfirm of doc.querySelectorAll('div[role="presentation"] p')) {
            if (travelConfirm.textContent == 'Travel') {
                travelConfirm.click(); await ns.sleep(200);
                break;
            }
        }

        try {
            doc.querySelector('div[role="presentation"] button').click();
        } catch (error) {

        }

        city.click(); await ns.sleep(200);

        for (const uni of doc.querySelectorAll('div#root span')) {
            if (uni.textContent == 'U') {
                uni.click(); await ns.sleep(200);
                break;
            }
        }

        for (const course of doc.querySelectorAll('div#root button')) {
            if (course.textContent == 'Take Algorithms course ($-1.280k / sec)' || course.textContent == 'Take Algorithms course($ - 960.000 / sec)') {
                course.click(); await ns.sleep(200);
                break;
            }
        }

        for (const closeCourse of doc.querySelectorAll('div#root button')) {
            if (closeCourse.textContent == 'Do something else simultaneously') {
                closeCourse.click(); await ns.sleep(200);
                break;
            }
        }
    }

    let buyCounter = 120;
    let prevMaxMoneyHost = '';
    ns.write('autoHack.txt', '', 'w');

    while (true) {
        // Buy all available exploits
        // if (buyCounter == 120) {
        //     terminalCommand("buyAll");
        //     buyCounter = 0;
        // }
        // ++buyCounter;

        // Buy more servers
        let serverRAM = 8;
        if (ns.getPurchasedServerCost(serverRAM) < ns.getServerMoneyAvailable('home') * 0.2) {
            ns.purchaseServer('home', serverRAM);
        }

        // Upgrade RAM
        for (const server of ns.getPurchasedServers()) {
            const serverStats = analyzeServer(server);
            if (serverStats.RAM < 128) {
                if (ns.getPurchasedServerUpgradeCost(serverStats.name, serverStats.RAM * 2) < ns.getServerMoneyAvailable('home') * 0.2) {
                    ns.upgradePurchasedServer(serverStats.name, serverStats.RAM * 2);
                }
            }
        }

        // Analyze all available hosts
        let network = scanServer('home');

        // NUKE if possible
        for (let host of network) {
            ns.scp(['grow.js', 'weaken.js', 'hack.js'], host.name, 'home');
            if (!host.rootAccess && host.nukeAvailable) {
                for (const exploit of EXPLOITS) {
                    if (ns.fileExists(exploit.name)) eval(`${exploit.function}('${host.name}')`);
                }
                ns.nuke(host.name);
                host = analyzeServer(host.name);
            }
        }

        // Find host with most money
        let maxMoneyHost = network[1];
        for (const host of network) {
            if (host.name != 'home' && host.rootAccess) {
                if (host.maxMoney > maxMoneyHost.maxMoney && host.currentMoney > 0 && host.requiredHacking < ns.getHackingLevel() / 3) {
                    maxMoneyHost = host;
                }
            }
        }

        // clear log file if max money host changed
        if (maxMoneyHost.name != prevMaxMoneyHost) {
            ns.write('autoHackMax.txt', '', 'w');
            prevMaxMoneyHost = maxMoneyHost.name;
        }

        //  TODO: convert to global RAM usage, steal 50%, regrow, weaken

        // Hack servers
        for (let i = 1; i < network.length; i++) {
            const host = network[i];
            if (!ns.scriptRunning('grow.js', host.name) && !ns.scriptRunning('weaken.js', host.name) && !ns.scriptRunning('hack.js', host.name) && host.rootAccess && host.RAM > 0) {
                if (host.name != 'home' && host.currentMoney > 0) {
                    const freeRAM = ns.getServerMaxRam(host.name) - ns.getServerUsedRam(host.name);
                    const hackRAM = ns.getScriptRam('hack.js', host.name);
                    const growRAM = ns.getScriptRam('grow.js', host.name);
                    const weakenRAM = ns.getScriptRam('weaken.js', host.name);
                    const hackTime = ns.getHackTime(host.name);
                    const growTime = ns.getGrowTime(host.name);
                    const weakenTime = ns.getWeakenTime(host.name);
                    const hackWait = hackTime <= growTime ? growTime - hackTime + 50 : 0;
                    const hackAnalyze = ns.hackAnalyze(host.name);
                    const growThreadsNeededPerHack = Math.ceil(ns.growthAnalyze(host.name, 1 + hackAnalyze));

                    let hackThreads = Math.floor(freeRAM / (hackRAM + growRAM * growThreadsNeededPerHack));
                    hackThreads = hackThreads == 0 ? 1 : hackThreads;
                    const growThreads = Math.floor((freeRAM - hackThreads * hackRAM) / growRAM);
                    const growLoops = Math.ceil(growThreadsNeededPerHack / growThreads);
                    const weakenThreads = Math.floor(freeRAM / weakenRAM);
                    const log = `host:${host.name} target:${host.name} | security:${(host.currentSecurity / host.minSecurity * 100).toFixed(0)}% | money:${(host.currentMoney / host.maxMoney * 100).toFixed(0)}% | hThreads:${hackThreads} hTime:${((growTime * (growLoops - 1) + hackWait + hackTime) / 60000).toFixed(2)}m | gThreads:${growThreads} gLoops:${growLoops} gTime:${(growTime * growLoops / 60000).toFixed(2)}m`;
                    ns.print(log);
                    ns.write('autoHack.txt', log + '\n', 'a');

                    if (host.currentSecurity / host.minSecurity > 2) {
                        if (weakenThreads > 0) {
                            ns.exec('weaken.js', host.name, weakenThreads, host.name);
                        }
                    } else {
                        ns.exec('grow.js', host.name, growThreads, host.name, growLoops);
                        ns.exec('hack.js', host.name, hackThreads, host.name, growTime * (growLoops - 1) + hackWait);
                    }
                } else {
                    const freeRAM = ns.getServerMaxRam(host.name) - ns.getServerUsedRam(host.name) - (host.name == 'home' ? 1.8 : 0);
                    const hackRAM = ns.getScriptRam('hack.js', host.name);
                    const growRAM = ns.getScriptRam('grow.js', host.name);
                    const weakenRAM = ns.getScriptRam('weaken.js', host.name);
                    const hackTime = ns.getHackTime(maxMoneyHost.name);
                    const growTime = ns.getGrowTime(maxMoneyHost.name);
                    const weakenTime = ns.getWeakenTime(maxMoneyHost.name);
                    const hackWait = hackTime <= growTime ? growTime - hackTime + 50 : 0;
                    const hackAnalyze = ns.hackAnalyze(maxMoneyHost.name);
                    const growThreadsNeededPerHack = Math.ceil(ns.growthAnalyze(maxMoneyHost.name, 1 + hackAnalyze));

                    let hackThreads = Math.floor(freeRAM / (hackRAM + growRAM * growThreadsNeededPerHack));
                    hackThreads = hackThreads == 0 ? 1 : hackThreads;
                    const growThreads = Math.floor((freeRAM - hackThreads * hackRAM) / growRAM);
                    // ns.tprint(`Math.floor((${freeRAM} - ${hackThreads} * ${hackRAM}) / ${growRAM}) = ${growThreads}`);
                    const growLoops = Math.ceil(growThreadsNeededPerHack / growThreads);
                    const weakenThreads = Math.floor(freeRAM / weakenRAM);
                    const log = `host:${host.name} target:${maxMoneyHost.name} | security:${(maxMoneyHost.currentSecurity / maxMoneyHost.minSecurity * 100).toFixed(0)}% | money:${(maxMoneyHost.currentMoney / maxMoneyHost.maxMoney * 100).toFixed(0)}% | hThreads:${hackThreads} hTime:${((growTime * (growLoops - 1) + hackWait + hackTime) / 60000).toFixed(2)}m | gThreads:${growThreads} gLoops:${growLoops} gTime:${(growTime * growLoops / 60000).toFixed(2)}m`; // | wThreads:${weakenThreads} wTime:${(weakenTime / 60000).toFixed(2)}m`;
                    ns.print(log);
                    ns.write('autoHackMax.txt', log + '\n', 'a');

                    // ns.tprint(`${maxMoneyHost.currentSecurity} / ${maxMoneyHost.minSecurity} > 5 = ${maxMoneyHost.currentSecurity / maxMoneyHost.minSecurity > 5}`)
                    if (maxMoneyHost.currentSecurity / maxMoneyHost.minSecurity > 5) {
                        if (weakenThreads > 0) {
                            ns.exec('weaken.js', host.name, weakenThreads, maxMoneyHost.name);
                        }
                    } else {
                        if (maxMoneyHost.currentMoney / maxMoneyHost.maxMoney < 0.2) {
                            ns.exec('grow.js', host.name, Math.floor(freeRAM / growRAM), maxMoneyHost.name);
                        } else {
                            ns.exec('grow.js', host.name, growThreads, maxMoneyHost.name, growLoops);
                            ns.exec('hack.js', host.name, hackThreads, maxMoneyHost.name, growTime * (growLoops - 1) + hackWait);
                        }
                    }
                }
            }
        }

        // Sleep before next loop
        await ns.sleep(1000);
    }
}