// Live updates through Pusher Channels (or a Soketi server): an order's status sent to its
// channels, a batch, who is on the presence channel, the auth answers a browser's pusher-js asks
// for, and a webhook from Pusher checked before it is believed.
//
//   PUSHER_APP_ID=... PUSHER_KEY=... PUSHER_SECRET=... PUSHER_HOST=https://api-eu.pusher.com quillo run pusher.qwe
//   then open http://localhost:8080/trigger, /batch, /channels, /auth and /webhook-check
//
// It needs a Pusher app (the dashboard's App Keys), or Soketi: PUSHER_HOST=http://127.0.0.1:6001.
// Point the app's webhooks at POST /pusher/webhook, and pusher-js's channelAuthorization at
// POST /pusher/auth.

use "pusher.qwer"

let app = {
    app_id: env("PUSHER_APP_ID", ""),
    key: env("PUSHER_KEY", ""),
    secret: env("PUSHER_SECRET", ""),
    host: env("PUSHER_HOST", "https://api-mt1.pusher.com"),
    timeout_ms: 5000,
}

// An order shipped: the shop's private channel and the public news feed hear it.
route GET /trigger = {
    sent: pusher_trigger(app, ["private-orders", "news"], "order-shipped", { order: 1001, carrier: "DHL" }, { info: "subscription_count" })?,
}

route GET /batch = {
    sent: pusher_trigger_batch(app, [
        { channel: "private-orders", name: "order-paid", data: { order: 1002 } },
        { channel: "presence-lobby", name: "announcement", data: "Doors open at 9" },
    ])?,
}

route GET /channels = {
    presence: pusher_channels(app, { prefix: "presence-", info: "user_count" })?.channels,
    lobby: pusher_channel(app, "presence-lobby", ["user_count", "subscription_count"])?,
    users: pusher_users(app, "presence-lobby")?,
    not_presence: pusher_channel(app, "news", "user_count").error,
}

// What pusher-js posts here (socket_id and channel_name) is answered with a signature, once the
// visitor is known to be allowed on that channel.
route POST /pusher/auth {
    let f = request.json ?? request.form
    if f.socket_id == null || f.channel_name == null { reply 400 "send socket_id and channel_name" }
    let user = { user_id: "7", user_info: { name: "Ada" } }
    pusher_auth(app, f.socket_id, f.channel_name, user)?
}

// Pusher's own examples (key 278d425bdf160c739803, secret 7ad3773142a6692b25b8): the plugin's
// signatures must come out as the documentation's.
let docs = { app_id: "3", key: "278d425bdf160c739803", secret: "7ad3773142a6692b25b8", host: "api.pusherapp.com" }
route GET /auth = {
    private: pusher_auth(docs, "1234.1234", "private-foobar", null)?.auth,
    presence: pusher_auth(docs, "1234.1234", "presence-foobar", { user_id: 10, user_info: { name: "Mr. Channels" } })?,
    rest: pusher_sign(docs, "POST", "/apps/3/events", {}, "{\"name\":\"foo\",\"channels\":[\"project-3\"],\"data\":\"{\\\"some\\\":\\\"data\\\"}\"}", 1353088179)?,
    public: pusher_auth(docs, "1234.1234", "news", null).error,
}

// Webhooks: believed only when signed with the app's secret.
route POST /pusher/webhook {
    let hook = pusher_webhook(app, request.headers, request.body)
    if is_err(hook) { reply 401 hook.error }
    { got: len(hook.events) }
}

// The check above, on a body signed here, and on the same body changed after signing.
route GET /webhook-check {
    let body = json.stringify({ time_ms: 1730000000000, events: [{ name: "channel_occupied", channel: "private-orders" }] })
    let headers = { "x-pusher-key": app.key, "x-pusher-signature": pusher_webhook_sign(app, body)? }
    {
        good: pusher_webhook(app, headers, body)?.events,
        tampered: pusher_webhook(app, headers, body + " ").error,
    }
}
