#lang syndicate

(require racket/port)
(require markdown)

(require/activate syndicate/reload)
(require/activate syndicate/supervise)
(require/activate "trust.rkt")

(require "protocol.rkt")
(require "duplicate.rkt")
(require "util.rkt")

(define (user-in-conversation? who cid)
  (immediate-query [query-value #f (in-conversation cid who) #t]))

(supervise
 (spawn #:name 'take-conversation-instructions
        (stop-when-reloaded)

        (on (message (api (session $creator _) (create-resource (? conversation? $c))))
            (when (equal? creator (conversation-creator c))
              (send! (create-resource c))))
        (on (message (api (session $updater _) (update-resource (? conversation? $c))))
            (when (user-in-conversation? updater (conversation-id c))
              (send! (update-resource c))))
        (on (message (api (session $creator _) (delete-resource (? conversation? $c))))
            (when (equal? creator (conversation-creator c))
              (send! (delete-resource c))))

        (on (message (api (session $joiner _) (create-resource (? in-conversation? $i))))
            (when (equal? joiner (in-conversation-member i))
              (send! (create-resource i))))
        (on (message (api (session $leaver _) (delete-resource (? in-conversation? $i))))
            (when (equal? leaver (in-conversation-member i))
              (send! (delete-resource i))))

        (on (message (api (session $inviter _) (create-resource (? invitation? $i))))
            (when (equal? inviter (invitation-inviter i))
              (send! (create-resource i))))
        (on (message (api (session $who _) (delete-resource (? invitation? $i))))
            (when (or (equal? who (invitation-inviter i))
                      (equal? who (invitation-invitee i)))
              (send! (delete-resource i))))

        (on (message (api (session $who _) (create-resource (? post? $p))))
            (when (and (user-in-conversation? who (post-conversation-id p))
                       (equal? who (post-author p)))
              (send! (create-resource p))))
        (on (message (api (session $who _) (update-resource (? post? $p))))
            (when (equal? who (post-author p))
              (send! (update-resource p))))
        (on (message (api (session $who _) (delete-resource (? post? $p))))
            (when (equal? who (post-author p))
              (send! (delete-resource p))))))

(supervise
 (spawn #:name 'relay-conversation-state
        (stop-when-reloaded)

        (during (invitation $cid $inviter $invitee)
          (assert (api (session invitee _) (invitation cid inviter invitee)))
          (during ($ c (conversation cid _ _ _))
            (assert (api (session invitee _) c))))

        (during (in-conversation $cid $who)
          (during ($ i (invitation cid _ _))
            (assert (api (session who _) i)))
          (during ($ i (in-conversation cid _))
            (assert (api (session who _) i)))
          (during ($ c (conversation cid _ _ _))
            (assert (api (session who _) c)))
          (during ($ p (post _ _ cid _ _))
            (assert (api (session who _) p))))))

(supervise
 (spawn #:name 'conversation-factory
        (stop-when-reloaded)
        (on (message (create-resource ($ c0 (conversation $cid $title0 $creator $blurb0))))
            (spawn #:name c0
                   (field [title title0]
                          [blurb blurb0])
                   (define/dataflow c (conversation cid (title) creator (blurb)))
                   (on-start (log-info "~v created" (c)))
                   (on-stop (log-info "~v deleted" (c)))
                   (assert (c))
                   (stop-when-duplicate (list 'conversation cid))
                   (stop-when (message (delete-resource (conversation cid _ _ _))))
                   (on (message (update-resource (conversation cid $newtitle _ $newblurb)))
                       (title newtitle)
                       (blurb newblurb))))))

(supervise
 (spawn #:name 'in-conversation-factory
        (stop-when-reloaded)
        (on (message (create-resource ($ i (in-conversation $cid $who))))
            (spawn #:name i
                   (on-start (log-info "~s joins conversation ~a" who cid))
                   (on-stop (log-info "~s leaves conversation ~a" who cid))
                   (assert i)
                   (stop-when-duplicate i)
                   (stop-when (message (delete-resource i)))
                   (stop-when (message (delete-resource (conversation cid _ _ _))))))))

(supervise
 (spawn #:name 'invitation-factory
        (stop-when-reloaded)
        (on (message (create-resource ($ i (invitation $cid $inviter $invitee))))
            (spawn #:name i
                   (on-start (log-info "~s invited to conversation ~a by ~s" invitee cid inviter))
                   (on-stop (log-info "invitation of ~s to conversation ~a by ~s retracted"
                                      invitee cid inviter))
                   (assert i)
                   (stop-when-duplicate i)
                   (stop-when (message (delete-resource i)))
                   (stop-when (message (delete-resource (conversation cid _ _ _))))
                   (stop-when (asserted (in-conversation cid invitee)))))))

(supervise
 (spawn #:name 'post-factory
        (stop-when-reloaded)
        (on (message (create-resource
                      ($ p0 (post $pid $timestamp $cid $author $items0))))
            (spawn #:name p0
                   (field [items items0])
                   (define/dataflow p (post pid timestamp cid author (items)))
                   (assert (p))
                   (stop-when-duplicate (list 'post cid pid))
                   (stop-when (message (delete-resource (post pid _ cid _ _))))
                   (stop-when (message (delete-resource (conversation cid _ _ _))))
                   (on (message (update-resource (post pid _ cid _ $newitems)))
                       (items newitems))))))

(supervise
 (spawn #:name 'conversation:questions
        (stop-when-reloaded)
        ;; TODO: CHECK THE FOLLOWING: When the `invitation` vanishes (due to satisfaction
        ;; or rejection), this should remove the question from all eligible answerers at once
        (during (invitation $cid $inviter $invitee)
          ;; `inviter` has invited `invitee` to conversation `cid`...
          (define qid (random-hex-string 32)) ;; Fix qid and timestamp even as title/creator vary
          (define timestamp (current-seconds))
          (during (conversation cid $title $creator _)
            ;; ...and it exists...
            (during (permitted invitee inviter (p:follow invitee) _)
              ;; ...and they are permitted to do so
              (assert (question qid timestamp "q-invitation" invitee
                                (format "Invitation from ~a" inviter)
                                (with-output-to-string
                                  (lambda ()
                                    (display-xexpr
                                     `(div
                                       (p "You have been invited by " (b ,inviter)
                                          " to join a conversation started by " (b ,creator) ".")
                                       (p "The conversation is titled "
                                          (i "\"" ,title "\"") ".")))))
                                (option-question (list (list "join" "Join conversation")
                                                       (list "decline" "Decline invitation")))))
              (stop-when (asserted (answer qid $v))
                         (match v
                           ["join"
                            (send! (create-resource (in-conversation cid invitee)))]
                           ["decline"
                            (send! (delete-resource (invitation cid inviter invitee)))])))))))
