// Pushes to iPhones through Apple's APNs: an alert, a replaceable score with a collapse id, a silent
// background wake-up, one message to many devices, and Apple's refusals read so that dead tokens can
// be dropped.
//
//   APNS_KEY="$(cat AuthKey_ABC1234567.p8)" APNS_KEY_ID=ABC1234567 APNS_TEAM_ID=DEF1234567 APNS_TOPIC=com.example.app quillo run apns.qwe
//   then open http://localhost:8080/alert, /score, /background, /many, /refused and /offline
//
// Development builds use the sandbox: APNS_HOST=api.sandbox.push.apple.com, or the option sandbox.
// DEVICE_TOKEN is what the app got from registerForRemoteNotifications.

use "apns.qwer"

let DEVICE = env("DEVICE_TOKEN", "")

route GET /alert {
    let p = apns_alert("Order shipped", "Your blue mug is on its way", { badge: 1, sound: "default", category: "ORDER", thread_id: "orders", data: { order: 1001 } })?
    let r = apns_send(DEVICE, p, {})?
    { ok: r.ok, status: r.status, id: len(r.apns_id) }
}

// The same collapse id: a device off-line gets only the last.
route GET /score {
    let a = apns_send(DEVICE, apns_alert("Score", "1 - 0", {})?, { collapse_id: "match-7" })?
    let b = apns_send(DEVICE, apns_alert("Score", "2 - 0", {})?, { collapse_id: "match-7", expiration: 0 })?
    { sent: a.ok && b.ok }
}

route GET /background {
    let r = apns_send(DEVICE, apns_background({ sync: "orders" })?, {})?
    let voip = apns_send(DEVICE, apns_alert("Call", "Ada", {})?, { push_type: "voip" })?
    { ok: r.ok, voip: voip.ok, too_urgent: apns_send(DEVICE, apns_background(null)?, { priority: 10 }).error }
}

route GET /many {
    let dead = repeat("dead", 16)
    let wrong = repeat("0bad", 16)
    let r = apns_send_many(map(range(0, 150), (i) => DEVICE) + [dead, wrong], apns_alert("Sale", "Today only", {})?, {})?
    { sent: r.sent, failed: r.failed, drop: r.remove == [dead, wrong] }
}

route GET /refused {
    let gone = apns_send(repeat("dead", 16), apns_alert("Hi", "", {})?, {})?
    let bad = apns_send(repeat("0bad", 16), apns_alert("Hi", "", {})?, {})?
    let other = apns_send(DEVICE, apns_alert("Hi", "", {})?, { topic: "com.example.other" })?
    {
        gone: gone.reason, gone_drop: gone.remove_token, since: gone.gone_since_ms,
        bad: bad.reason, bad_means: bad.meaning, bad_drop: bad.remove_token,
        other: other.reason, other_drop: other.remove_token,
    }
}

// A token Apple calls expired is renewed once, and the push goes through.
route GET /renew {
    let r = apns_send(DEVICE, apns_alert("expire my token", "", {})?, {})?
    let later = apns_send(DEVICE, apns_alert("retry me", "", {})?, {})?
    { ok: r.ok, retried: later.ok }
}

// No network: what is built, and what is refused before it goes.
route GET /offline {
    let p = apns_alert("Hi", "There", { subtitle: "Sub", mutable_content: true, interruption_level: "time-sensitive", data: { deep_link: "/orders/1" } })?
    {
        payload: p,
        silent: apns_background({ a: 1 })?,
        clean: apns_clean_token("<ABCD 1234>"),
        big: apns_send(DEVICE, apns_alert("x", repeat("y", 4100), {})?, {}).error,
        long_collapse: apns_send(DEVICE, p, { collapse_id: repeat("c", 65) }).error,
        bad_device: apns_send("abc", p, {}).error,
        bad_type: apns_send(DEVICE, p, { push_type: "banner" }).error,
        no_aps: apns_send(DEVICE, { hello: 1 }, {}).error,
        empty: apns_alert("", "", {}).error,
        own_aps: apns_alert("x", "", { data: { aps: 1 } }).error,
        bad_key: apns_token({ key: "not a key" }).error,
        token_parts: len(split(apns_token({})?, ".")),
    }
}
