import { isOnboardingComplete, loadOwnerConfig, ownerConfigExists, saveOwnerConfig } from "../config/index.js";
import { TelegramOnboardingHandler } from "../onboarding/telegram-onboarding.js";
import { sendOnboardingEmail } from "../onboarding/send-invite.js";
import { OnboardingServiceClient } from "../onboarding/onboarding-service-client.js";
import { waitForRemoteOwner } from "../onboarding/remote-set-owner.js";
import { generateVerificationCode } from "../onboarding/set-owner.js";
import { getCurrentVersion } from "../utils/version-check.js";
import { logger } from "../logger.js";
function createOnboardingClient(config) {
    if (!config.onboardingServiceUrl || !config.visionclawServiceApiKey)
        return null;
    return new OnboardingServiceClient(config.onboardingServiceUrl, config.agentName, config.visionclawServiceApiKey);
}
function buildOnboardingHeartbeatPayload(config) {
    const payload = {
        version: getCurrentVersion(),
        model: config.model,
        provider: config.provider,
        uptime: Math.round(process.uptime()),
        busy: false,
        onboardingComplete: false,
    };
    // Forward owner info (including telegramChatId) once the owner is set
    // so the server can store the chat ID on the agent row even before
    // onboarding completion is recorded.
    if (ownerConfigExists()) {
        try {
            const owner = loadOwnerConfig();
            if (owner.ownerEmail)
                payload.ownerEmail = owner.ownerEmail;
            if (owner.ownerName)
                payload.ownerName = owner.ownerName;
            if (owner.telegramChatId !== undefined) {
                payload.ownerChatId = String(owner.telegramChatId);
            }
        }
        catch {
            /* best-effort */
        }
    }
    return payload;
}
/**
 * Run the onboarding gate. If onboarding is not yet complete, this drives
 * the restricted Telegram onboarding loop and then returns so the normal
 * agent loop can proceed without a process restart.
 *
 * @returns `true` if onboarding was performed during this call (i.e. the
 *          agent just transitioned from incomplete → complete), `false` if
 *          onboarding was already complete on entry.
 */
export async function runOnboardingGate(config, channelManager, messageQueue) {
    if (isOnboardingComplete())
        return false;
    const onboardingClient = createOnboardingClient(config);
    // Send an initial heartbeat so the onboarding server knows we're online
    // before any potentially slow steps (email sending, etc.).
    if (onboardingClient) {
        await onboardingClient.heartbeat(buildOnboardingHeartbeatPayload(config));
    }
    if (!ownerConfigExists()) {
        if (onboardingClient) {
            logger.system("No owner configured. Polling onboarding service for owner email...");
            await waitForRemoteOwner(onboardingClient, config);
        }
        else {
            logger.system("No owner configured. Run 'visionclaw set-owner --email <email>' to set up the owner.");
            process.exit(1);
        }
    }
    const partialOwner = loadOwnerConfig();
    logger.system("Owner onboarding not complete. Entering onboarding mode...");
    if (!partialOwner.onboardingCodeVerified) {
        logger.system("Sending onboarding email...");
        try {
            const EMAIL_TIMEOUT_MS = 30_000;
            await Promise.race([
                sendOnboardingEmail(config, partialOwner),
                new Promise((_resolve, reject) => {
                    setTimeout(() => reject(new Error("Timed out after 30s")), EMAIL_TIMEOUT_MS);
                }),
            ]);
            if (onboardingClient) {
                await onboardingClient.notifyEmailSent();
            }
        }
        catch (err) {
            logger.err(`Failed to send onboarding email: ${err instanceof Error ? err.message : String(err)}`);
        }
    }
    else {
        logger.system("Code already verified — finalizing onboarding...");
    }
    const botToken = config.channels.telegram?.botToken ?? "";
    const handler = new TelegramOnboardingHandler(partialOwner, config, botToken, channelManager);
    // Start background heartbeat so the onboarding server tracks us as online
    // and we can detect owner email changes or resend requests.
    let heartbeatTimer;
    if (onboardingClient) {
        heartbeatTimer = setInterval(() => {
            void (async () => {
                const result = await onboardingClient.heartbeat(buildOnboardingHeartbeatPayload(config));
                if (!result)
                    return;
                // Sync owner email/name if changed on the server (e.g. owner re-submitted
                // the form with a different email before activating Telegram).
                const currentOwner = loadOwnerConfig();
                let ownerUpdated = false;
                if (result.ownerEmail && result.ownerEmail !== currentOwner.ownerEmail) {
                    logger.system(`Owner email changed on server: ${currentOwner.ownerEmail} → ${result.ownerEmail}. Updating local config...`);
                    currentOwner.ownerEmail = result.ownerEmail;
                    if (result.ownerName)
                        currentOwner.ownerName = result.ownerName;
                    // Regenerate verification code since the email changed
                    currentOwner.onboardingCode = generateVerificationCode();
                    currentOwner.onboardingCodeVerified = false;
                    saveOwnerConfig(currentOwner);
                    // Update the handler's reference so it uses the new code
                    handler.updateOwnerConfig(currentOwner);
                    ownerUpdated = true;
                }
                if (result.resendRequested || ownerUpdated) {
                    const reason = ownerUpdated ? "owner email changed" : "resend requested";
                    logger.system(`Re-sending onboarding email (${reason})...`);
                    try {
                        await sendOnboardingEmail(config, loadOwnerConfig());
                        await onboardingClient.notifyEmailSent();
                        logger.system("Onboarding email re-sent successfully.");
                    }
                    catch (err) {
                        logger.err(`Failed to re-send onboarding email: ${err instanceof Error ? err.message : String(err)}`);
                    }
                }
            })();
        }, 10_000);
    }
    const sendReply = async (chatId, text) => {
        await channelManager.sendMessage("telegram", String(chatId), text);
    };
    await new Promise((resolve) => {
        const onMessage = () => {
            void (async () => {
                while (!messageQueue.isEmpty()) {
                    const msg = messageQueue.dequeue();
                    if (!msg)
                        continue;
                    if (msg.channel !== "telegram") {
                        logger.debug(`Onboarding: ignoring ${msg.channel} message`);
                        continue;
                    }
                    logger.log("info", "incoming", `[onboarding] [${msg.channel}] ${msg.sender}: ${msg.text}`, {
                        channel: msg.channel,
                        sender: msg.sender,
                        text: msg.text,
                    });
                    const done = await handler.handleMessage(msg, sendReply);
                    if (done) {
                        channelManager.off("message", onMessage);
                        resolve();
                        return;
                    }
                }
            })();
        };
        channelManager.on("message", onMessage);
    });
    if (heartbeatTimer)
        clearInterval(heartbeatTimer);
    // Fresh onboarding establishes a new owner session, so we discard any
    // queued inbound work accumulated during onboarding before entering the
    // normal agent loop.
    messageQueue.clear();
    if (onboardingClient) {
        const finalOwner = loadOwnerConfig();
        await onboardingClient.notifyComplete({
            ownerEmail: finalOwner.ownerEmail,
            ownerName: finalOwner.ownerName,
            ownerChatId: finalOwner.telegramChatId !== undefined
                ? String(finalOwner.telegramChatId)
                : undefined,
        });
    }
    logger.system("Owner onboarding complete! Continuing to normal operation...\n");
    return true;
}
//# sourceMappingURL=onboarding-gate.js.map