/**
 * remote/gateway/gateway — the multi-platform Gateway.
 *
 * One running agent session, several messaging surfaces. The gateway:
 *   - subscribes to the `RemoteControlHub` event stream, renders each event
 *     for chat, and broadcasts it to every adapter;
 *   - receives inbound messages from every adapter, parses control commands
 *     (/approve, /reject, /whisper, /status) and otherwise enqueues the text
 *     into the hub's `InstructionInbox` — mid-run steering from any platform.
 *
 * This is the Hermes-style assistant gateway built entirely on the T1-T3
 * primitives: the same agent is reachable from the CLI, Telegram, Discord and
 * Slack at once, with no surface-specific agent logic.
 *
 * @public @since 2.14.0 — preste remote-control T4
 */
import { renderEventForChat } from "./render.js";
import { NOOP_LOGGER } from "./types.js";
const DEFAULT_GREETING = "🟢 preste gateway online — send a message to steer the agent. " +
    "Commands: /approve · /reject · /whisper <hint> · /status";
/** Create a Gateway over a RemoteControlHub. */
export function createGateway(opts) {
    const log = opts.log ?? NOOP_LOGGER;
    const renderOpts = { verbose: opts.verbose ?? false };
    const greeting = opts.greeting === false ? null : (opts.greeting ?? DEFAULT_GREETING);
    let started = false;
    let unsubscribe = null;
    /** Most recent HITL request id — the target of a bare /approve or /reject. */
    let lastPendingHitlId = null;
    async function deliverAll(text) {
        await Promise.all(opts.adapters.map((a) => a.deliver(text).catch((err) => {
            log(`${a.platform}: deliver failed — ${err.message}`);
        })));
    }
    function routeApproval(approved, by, text) {
        const channel = opts.approvalChannel;
        if (!channel) {
            log("gateway: approval command ignored — no HITL channel wired");
            return;
        }
        // "/approve <id>" targets an explicit request; bare "/approve" the latest.
        const parts = text.trim().split(/\s+/);
        const explicitId = parts.length > 1 ? parts[1] : undefined;
        const id = explicitId ?? lastPendingHitlId;
        if (!id) {
            log("gateway: approval command ignored — no pending HITL request");
            return;
        }
        if (!channel.resolve(id, approved, by)) {
            log(`gateway: HITL ${id} could not be resolved (already settled?)`);
        }
    }
    function onInbound(msg) {
        const source = `${msg.platform}:${msg.chatId}`;
        const text = msg.text.trim();
        if (text === "")
            return;
        const lower = text.toLowerCase();
        if (lower === "/approve" || lower === "/yes" || lower.startsWith("/approve ")) {
            routeApproval(true, source, text);
            return;
        }
        if (lower === "/reject" || lower === "/no" || lower.startsWith("/reject ")) {
            routeApproval(false, source, text);
            return;
        }
        if (lower.startsWith("/whisper ")) {
            opts.hub.inbox.enqueue(text.slice(9).trim(), source, true);
            return;
        }
        if (lower === "/status") {
            const s = opts.hub.state();
            void deliverAll(`📊 ${s.status} — step ${s.stepCount}, ` +
                `$${s.costUsd.toFixed(4)}, ${s.pendingHitl} pending approval(s)`);
            return;
        }
        // Plain message → steering instruction injected mid-run.
        opts.hub.inbox.enqueue(text, source, false);
    }
    function onEvent(event) {
        if (event.type === "hitl.request") {
            lastPendingHitlId = event.data.requestId;
        }
        else if (event.type === "hitl.resolved" && lastPendingHitlId === event.data.requestId) {
            lastPendingHitlId = null;
        }
        const text = renderEventForChat(event, renderOpts);
        if (text !== null)
            void deliverAll(text);
    }
    return {
        get platforms() {
            return opts.adapters.map((a) => a.platform);
        },
        async start() {
            if (started)
                return;
            started = true;
            unsubscribe = opts.hub.subscribe(onEvent);
            // One platform failing to start must never sink the others.
            await Promise.all(opts.adapters.map((a) => a.start(onInbound).catch((err) => {
                log(`${a.platform}: start failed — ${err.message}`);
            })));
            log(`gateway: up on ${opts.adapters.map((a) => a.platform).join(", ") || "(no adapters)"}`);
            if (greeting)
                await deliverAll(greeting);
        },
        async stop() {
            if (!started)
                return;
            started = false;
            if (unsubscribe) {
                unsubscribe();
                unsubscribe = null;
            }
            await Promise.all(opts.adapters.map((a) => a.stop().catch((err) => {
                log(`${a.platform}: stop failed — ${err.message}`);
            })));
        },
    };
}
//# sourceMappingURL=gateway.js.map