import { buildGatewayInstallPlan } from "../../commands/daemon-install-helpers.js";
import { DEFAULT_GATEWAY_DAEMON_RUNTIME, isGatewayDaemonRuntime, } from "../../commands/daemon-runtime.js";
import { randomToken } from "../../commands/onboard-helpers.js";
import { loadConfig, readConfigFileSnapshot, resolveGatewayPort, writeConfigFile, } from "../../config/config.js";
import { resolveIsNixMode } from "../../config/paths.js";
import { resolveGatewayService } from "../../daemon/service.js";
import { resolveGatewayAuth } from "../../gateway/auth.js";
import { defaultRuntime } from "../../runtime.js";
import { formatCliCommand } from "../command-format.js";
import { buildDaemonServiceSnapshot, createNullWriter, emitDaemonActionJson } from "./response.js";
import { parsePort } from "./shared.js";
export async function runDaemonInstall(opts) {
    const json = Boolean(opts.json);
    const warnings = [];
    const stdout = json ? createNullWriter() : process.stdout;
    const emit = (payload) => {
        if (!json) {
            return;
        }
        emitDaemonActionJson({ action: "install", ...payload });
    };
    const fail = (message) => {
        if (json) {
            emit({ ok: false, error: message, warnings: warnings.length ? warnings : undefined });
        }
        else {
            defaultRuntime.error(message);
        }
        defaultRuntime.exit(1);
    };
    if (resolveIsNixMode(process.env)) {
        fail("Nix mode detected; service install is disabled.");
        return;
    }
    const cfg = loadConfig();
    const portOverride = parsePort(opts.port);
    if (opts.port !== undefined && portOverride === null) {
        fail("Invalid port");
        return;
    }
    const port = portOverride ?? resolveGatewayPort(cfg);
    if (!Number.isFinite(port) || port <= 0) {
        fail("Invalid port");
        return;
    }
    const runtimeRaw = opts.runtime ? String(opts.runtime) : DEFAULT_GATEWAY_DAEMON_RUNTIME;
    if (!isGatewayDaemonRuntime(runtimeRaw)) {
        fail('Invalid --runtime (use "node" or "bun")');
        return;
    }
    const service = resolveGatewayService();
    let loaded = false;
    try {
        loaded = await service.isLoaded({ env: process.env });
    }
    catch (err) {
        fail(`Gateway service check failed: ${String(err)}`);
        return;
    }
    if (loaded) {
        if (!opts.force) {
            emit({
                ok: true,
                result: "already-installed",
                message: `Gateway service already ${service.loadedText}.`,
                service: buildDaemonServiceSnapshot(service, loaded),
                warnings: warnings.length ? warnings : undefined,
            });
            if (!json) {
                defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
                defaultRuntime.log(`Reinstall with: ${formatCliCommand("openclaw gateway install --force")}`);
            }
            return;
        }
    }
    // Resolve effective auth mode to determine if token auto-generation is needed.
    // Password-mode and Tailscale-only installs do not need a token.
    const resolvedAuth = resolveGatewayAuth({
        authConfig: cfg.gateway?.auth,
        tailscaleMode: cfg.gateway?.tailscale?.mode ?? "off",
    });
    const needsToken = resolvedAuth.mode === "token" && !resolvedAuth.token && !resolvedAuth.allowTailscale;
    let token = opts.token ||
        cfg.gateway?.auth?.token ||
        process.env.OPENCLAW_GATEWAY_TOKEN ||
        process.env.CLAWDBOT_GATEWAY_TOKEN;
    if (!token && needsToken) {
        token = randomToken();
        const warnMsg = "No gateway token found. Auto-generated one and saving to config.";
        if (json) {
            warnings.push(warnMsg);
        }
        else {
            defaultRuntime.log(warnMsg);
        }
        // Persist to config file so the gateway reads it at runtime
        // (launchd does not inherit shell env vars, and CLI tools also
        // read gateway.auth.token from config for gateway calls).
        try {
            const snapshot = await readConfigFileSnapshot();
            if (snapshot.exists && !snapshot.valid) {
                // Config file exists but is corrupt/unparseable — don't risk overwriting.
                // Token is still embedded in the plist EnvironmentVariables.
                const msg = "Warning: config file exists but is invalid; skipping token persistence.";
                if (json) {
                    warnings.push(msg);
                }
                else {
                    defaultRuntime.log(msg);
                }
            }
            else {
                const baseConfig = snapshot.exists ? snapshot.config : {};
                if (!baseConfig.gateway?.auth?.token) {
                    await writeConfigFile({
                        ...baseConfig,
                        gateway: {
                            ...baseConfig.gateway,
                            auth: {
                                ...baseConfig.gateway?.auth,
                                mode: baseConfig.gateway?.auth?.mode ?? "token",
                                token,
                            },
                        },
                    });
                }
                else {
                    // Another process wrote a token between loadConfig() and now.
                    token = baseConfig.gateway.auth.token;
                }
            }
        }
        catch (err) {
            // Non-fatal: token is still embedded in the plist EnvironmentVariables.
            const msg = `Warning: could not persist token to config: ${String(err)}`;
            if (json) {
                warnings.push(msg);
            }
            else {
                defaultRuntime.log(msg);
            }
        }
    }
    const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
        env: process.env,
        port,
        token,
        runtime: runtimeRaw,
        warn: (message) => {
            if (json) {
                warnings.push(message);
            }
            else {
                defaultRuntime.log(message);
            }
        },
        config: cfg,
    });
    try {
        await service.install({
            env: process.env,
            stdout,
            programArguments,
            workingDirectory,
            environment,
        });
    }
    catch (err) {
        fail(`Gateway install failed: ${String(err)}`);
        return;
    }
    let installed = true;
    try {
        installed = await service.isLoaded({ env: process.env });
    }
    catch {
        installed = true;
    }
    emit({
        ok: true,
        result: "installed",
        service: buildDaemonServiceSnapshot(service, installed),
        warnings: warnings.length ? warnings : undefined,
    });
}
