import { createBridgeApplication } from "./application/createApplication.js";
import { assertLoopbackBinding, loadBridgeConfig, summarizeSeedConfig, } from "./config/bridgeConfig.js";
import { createHttpServer } from "./http/createServer.js";
import { applySeed } from "./persistence/seed/applySeed.js";
import { createCommandEventSubscriber } from "./runs/commandEventSubscriber.js";
import { startCommandPollTicker } from "./runs/commandPollTicker.js";
import { startSyncTicker } from "./runs/syncTicker.js";
const config = loadBridgeConfig();
assertLoopbackBinding(config);
process.on("unhandledRejection", (reason) => {
    console.error("patchtrail-agent-bridge unhandledRejection:", reason);
    process.exit(1);
});
process.on("uncaughtException", (error) => {
    console.error("patchtrail-agent-bridge uncaughtException:", error);
    process.exit(1);
});
const app = createBridgeApplication();
const seedResult = applySeed(app.localStore, config.seed);
app.seedStatus.set(seedResult);
if (seedResult.enabled) {
    const summary = summarizeSeedConfig(config.seed);
    console.log(`patchtrail-agent-bridge seed applied: domains=${summary.enabledDomains.join(",")}`);
}
const server = createHttpServer(app, config);
const syncTicker = startSyncTicker({
    store: app.localStore,
    config: app.syncConfig,
    deviceSigner: (deviceId, hmacSignatureValue) => app.deviceKeyManager.signValue(deviceId, hmacSignatureValue),
    onError: (error) => {
        console.error("patchtrail-agent-bridge sync cycle failed:", error);
    },
});
void syncTicker.runNow();
// Command poller — the receiving half of the Layer 2 transport. Pulls
// commands the hosted backend has queued for this device, stores them in the
// local inbox, and acks them. The Layer 3 executor (app.remoteCommandExecutor)
// drains the inbox after each poll (afterPoll), enforcing the fail-closed gate
// (verify → default-deny category → risk tier → dispatch to existing routes).
// No-op while sync is disabled or no device is registered.
const commandPollTicker = startCommandPollTicker({
    store: app.localStore,
    config: app.syncConfig,
    deviceSigner: (deviceId, hmacSignatureValue) => app.deviceKeyManager.signValue(deviceId, hmacSignatureValue),
    afterPoll: () => app.remoteCommandExecutor.drainOnce().then(() => undefined),
    onError: (error) => {
        console.error("patchtrail-agent-bridge command poll cycle failed:", error);
    },
});
void commandPollTicker.runNow();
// SSE notification stream — when the backend signals work for this device, pull
// immediately rather than waiting for the next poll tick. A dropped stream just
// falls back to the durable poller, so failures are logged as bounded reason
// codes and the subscriber reconnects.
const commandEventSubscriber = createCommandEventSubscriber({
    store: app.localStore,
    config: app.syncConfig,
    deviceSigner: (deviceId, hmacSignatureValue) => app.deviceKeyManager.signValue(deviceId, hmacSignatureValue),
    onNotify: () => {
        void commandPollTicker.runNow();
    },
    onError: (reasonCode) => {
        console.error(`patchtrail-agent-bridge command events stream failed: ${reasonCode}`);
    },
});
commandEventSubscriber.ensureStarted();
server.listen(config.port, config.host, () => {
    const address = server.address();
    const renderedAddress = typeof address === "object" && address
        ? `${address.address}:${address.port}`
        : String(address);
    console.log(`patchtrail-agent-bridge listening on ${renderedAddress}`);
});
function shutdown(signal) {
    console.log(`received ${signal}; shutting down patchtrail-agent-bridge`);
    syncTicker.stop();
    commandPollTicker.stop();
    server.close((error) => {
        void (async () => {
            try {
                await commandEventSubscriber.stop();
                await app.runtimeEventIngestor.stop();
                await app.runtime.stop();
            }
            catch (shutdownError) {
                console.error(shutdownError);
                process.exit(1);
            }
            if (error) {
                console.error(error);
                process.exit(1);
            }
            process.exit(0);
        })();
    });
}
process.once("SIGINT", shutdown);
process.once("SIGTERM", shutdown);
