// A support inbox handled over IMAP: the folders, what is unread, a customer's reply read without
// marking it seen, an invoice's attachment listed, searches by sender and date, a message marked
// read and unread again, one archived, our answer kept in Sent, and a wait (IDLE) for new mail.
//
//   IMAP_URL=imaps://support%40example.com:password@imap.example.com quillo run imap.qwe
//   then open http://localhost:8080/unread, /read, /wait
//
// Needs an IMAP server at IMAP_URL (imaps://, or imap:// upgraded with STARTTLS); the user name and
// password are percent-encoded in the URL. IMAP_CA is a CA's PEM for a private certificate.

use "imap.qwer"

let URL = env("IMAP_URL", "imaps://support%40example.com:password@127.0.0.1")
let OPTS = { ca: env("IMAP_CA", ""), timeout_ms: 5000 }

route GET /folders = map(imap_folders(URL, OPTS)?, (f) => { name: f.name, special: f.special })

route GET /unread {
    let uids = imap_search(URL, "INBOX", { unseen: true }, OPTS)?
    let heads = imap_fetch(URL, "INBOX", uids, { headers_only: true, ca: OPTS.ca })?
    { count: len(uids), messages: map(heads, (m) => { uid: m.uid, from: m.from.email, subject: m.subject }) }
}

route GET /read {
    let m = imap_fetch(URL, "INBOX", 101, OPTS)?[0]
    { subject: m.subject, from: m.from.name, text: m.text, still_unread: !m.seen }
}

route GET /invoice {
    let m = imap_fetch(URL, "INBOX", 102, OPTS)?[0]
    { subject: m.subject, attachments: m.attachments, has_html: m.html != null }
}

route GET /search = {
    from_grace: imap_search(URL, "INBOX", { from: "grace@navy.example" }, OPTS)?,
    since_29th: imap_search(URL, "INBOX", { since: "2026-10-29" }, OPTS)?,
    invoice_unread: imap_search(URL, "INBOX", { subject: "invoice", unseen: true }, OPTS)?,
}

route GET /seen {
    imap_set_flags(URL, "INBOX", [101], ["seen", "flagged"], OPTS)?
    let after = imap_search(URL, "INBOX", { unseen: true }, OPTS)?
    imap_clear_flags(URL, "INBOX", [101], "seen", OPTS)?
    { unread_after_marking: after, unread_again: imap_search(URL, "INBOX", { unseen: true }, OPTS)?, flagged: imap_search(URL, "INBOX", { flagged: true }, OPTS)? }
}

route GET /archive {
    let moved = imap_move(URL, "INBOX", [103], "Archive", OPTS)?
    { moved, archive: imap_status(URL, "Archive", OPTS)?, inbox: imap_status(URL, "INBOX", OPTS)? }
}

// Our answer, kept in Sent as a mail client would after sending it.
route GET /reply {
    let answer = join([
        "From: Support <support@quillo.test>",
        "To: Ada Lovelace <ada@example.com>",
        "Subject: Re: Re: Your order #1042",
        "In-Reply-To: <reply-1042@example.com>",
        "",
        "Friday it is. We will ring twice.",
    ], "\r\n")
    let kept = imap_append(URL, "Sent", answer, { flags: ["seen"], ca: OPTS.ca })?
    { kept, sent: map(imap_fetch(URL, "Sent", kept.uid, OPTS)?, (m) => m.subject) }
}

// New mail: waits (IDLE) for anything after the newest message we know of.
route GET /wait {
    let newest = imap_status(URL, "INBOX", OPTS)?.uidnext - 1
    let fresh = imap_wait(URL, "INBOX", newest, 5000, OPTS)?
    { new: fresh, subjects: len(fresh) > 0 ? map(imap_fetch(URL, "INBOX", fresh, OPTS)?, (m) => m.subject) : [] }
}

route GET /wrong = { error: imap_folders(replace(URL, "p%40ss", "guess"), OPTS).error }
