text: (ns braid.rss.core
    "Extension to post updates from RSS feeds as messages in a given group"
    (:require
     [braid.core.api :as core]
     #?@(:clj
         [[braid.rss.server.db :as db]
          [braid.rss.server.fetching :as fetching]
          [braid.core.server.db.group :as group-db]
          [taoensso.timbre :as timbre]]
         :cljs
         [[braid.rss.client.views :as views]
          [re-frame.core :refer [dispatch]]])))
  
  (defn init!
    []
    #?(:clj
       (do
         (core/register-db-schema! db/schema)
  
         (core/register-server-message-handlers!
           {:braid.server.rss/load-feeds
            (fn [{user-id :user-id group-id :?data}]
              (when (group-db/user-in-group? user-id group-id)
                {:reply! {:braid/ok (db/group-feeds group-id)}}))
  
            :braid.server.rss/check-feed-url
            (fn [{user-id :user-id feed-url :?data}]
              (when user-id
                {:reply! {:braid/ok (fetching/feed-works? feed-url)}}))
  
            :braid.server.rss/add-feed
            (fn [{user-id :user-id {:keys [tag-ids feed-url group-id] :as new-feed} :?data}]
              (when (and (group-db/user-is-group-admin? user-id group-id)
                       (let [group-tags (into #{} (map :id) (group-db/group-tags group-id))]
                         (every? group-tags tag-ids)))
                (let [new-feed (assoc new-feed
                                      :id (java.util.UUID/randomUUID)
                                      :user-id user-id)]
                  {:db-run-txns! (db/add-rss-feed-txn new-feed)
                   :reply! {:braid/ok new-feed}})))
  
            :braid.server.rss/force-feed-run!
            (fn [{user-id :user-id feed-id :?data}]
              (let [feed (db/feed-by-id feed-id)]
                (when (some->> feed
                        :group-id
                        (group-db/user-is-group-admin? user-id))
                  (try
                    (fetching/update-feed! feed)
                    (catch clojure.lang.ExceptionInfo ex
                      (timbre/errorf "Failed to fetch feed %s: %s" feed
                                     (ex-data ex)))))))
  
            :braid.server.rss/retract-feed
            (fn [{user-id :user-id feed-id :?data}]
              (let [group-id (db/feed-group-id feed-id)]
                (when (group-db/user-is-group-admin? user-id group-id)
                  {:db-run-txns! (db/remove-feed-txn feed-id)
                   :reply! :braid/ok})))})
  
         (core/register-daily-job!
           (fn []
             (timbre/debugf "Running RSS job")
             (doseq [feed (db/all-feeds)]
               (timbre/debugf "Fetching %s" feed)
               (try
                 (fetching/update-feed! feed)
                 (catch clojure.lang.ExceptionInfo ex
                   (timbre/errorf "Bad feed %s: %s" feed
                                  (ex-data ex))))))))
      :cljs
       (do
         (core/register-group-setting! views/rss-feed-settings-view)
         (core/register-styles! [:.settings.rss-feeds
                                 [:.new-rss-feed
                                  [:label {:display "block"}]
                                  [:.error {:color "red"}]]])
         (core/register-state! {:rss/feeds {}} {:rss/feeds any?})
         (core/register-subs! {:rss/feeds
                               (fn [db [_ group-id]]
                                 (get-in db [:rss/feeds (or group-id
                                                            (db :open-group-id))]))})
         (core/register-events!
           {:rss/load-group-feeds
            (fn [_ [_ group-id]]
              {:websocket-send (list [:braid.server.rss/load-feeds group-id]
                                     5000
                                     (fn [reply]
                                       (if-let [resp (:braid/ok reply)]
                                         (dispatch [:rss/-store-feeds group-id resp])
                                         (.error js/console "Failed to load feeds "
                                                 (pr-str reply)))))})
  
            :rss/-store-feeds
            (fn [{db :db} [_ group-id feeds]]
              {:db (assoc-in db [:rss/feeds group-id] (set feeds))})
  
            :rss/-store-feed
            (fn [{db :db} [_ feed]]
              {:db (update-in db [:rss/feeds (feed :group-id)] (fnil conj #{}) feed)})
  
            :rss/-remove-feed
            (fn [{db :db} [_ feed]]
              {:db (update-in db [:rss/feeds (feed :group-id)] disj feed)})
  
            :rss/-check-feed-valid
            (fn [_ [_ feed-url on-complete]]
              {:websocket-send
               (list [:braid.server.rss/check-feed-url feed-url]
                     5000
                     (fn [reply]
                       (if (and (map? reply) (contains? reply :braid/ok))
                         (on-complete (:braid/ok reply))
                         (dispatch [:braid.notices/display!
                                    [::feed-url-check
                                     "Something went wrong validating the feed URL"
                                     :error]]))))})
  
            :rss/check-feed-valid
            (fn [_ [_ feed-url on-complete]]
              {:dispatch-debounce
               [::check-feed [:rss/-check-feed-valid feed-url on-complete]
                1000]})
  
            :rss/add-feed
            (fn [_ [_ new-feed]]
              {:websocket-send (list [:braid.server.rss/add-feed new-feed]
                                     5000
                                     (fn [reply]
                                       (if-let [resp (:braid/ok reply)]
                                         (dispatch [:rss/-store-feed resp])
                                         (.error js/console "Error adding feed"
                                                 (pr-str reply)))))})
            :rss/retract-feed
            (fn [_ [_ feed]]
              {:websocket-send (list [:braid.server.rss/retract-feed (feed :id)]
                                     5000
                                     (fn [reply]
                                       (when (= :braid/ok reply)
                                         (dispatch [:rss/-remove-feed feed]))))})
  
            :rss/force-feed-run
            (fn [_ [_ feed-id]]
              {:websocket-send (list [:braid.server.rss/force-feed-run! feed-id])})}))))
  