/api/timeline-event endpoints.

(ns metabase.api.timeline-event
  (:require
   [compojure.core :refer [DELETE GET POST PUT]]
   [metabase.analytics.snowplow :as snowplow]
   [metabase.api.common :as api]
   [metabase.models.collection :as collection]
   [metabase.models.timeline :refer [Timeline]]
   [metabase.models.timeline-event
    :as timeline-event
    :refer [TimelineEvent]]
   [metabase.util :as u]
   [metabase.util.date-2 :as u.date]
   [metabase.util.i18n :refer [tru]]
   [metabase.util.malli.schema :as ms]
   [toucan2.core :as t2]))

/

(api/defendpoint POST 
  "Create a new [[TimelineEvent]]."
  [:as {{:keys [name description timestamp time_matters timezone icon timeline_id source question_id archived] :as body} :body}]
  {name         ms/NonBlankString
   description  [:maybe :string]
   timestamp    ms/TemporalString
   time_matters [:maybe :boolean]
   timezone     :string
   icon         [:maybe timeline-event/Icon]
   timeline_id  ms/PositiveInt
   source       [:maybe timeline-event/Source]
   question_id  [:maybe ms/PositiveInt]
   archived     [:maybe :boolean]}
  ;; deliberately not using api/check-404 so we can have a useful error message.
  (let [timeline (t2/select-one Timeline :id timeline_id)]
    (when-not timeline
      (throw (ex-info (tru "Timeline with id {0} not found" timeline_id)
                      {:status-code 404})))
    (collection/check-write-perms-for-collection (:collection_id timeline))
    ;; todo: revision system
    (let [parsed   (if (nil? timestamp)
                     (throw (ex-info (tru "Timestamp cannot be null") {:status-code 400}))
                     (u.date/parse timestamp))
          tl-event (merge (dissoc body :source :question_id)
                          {:creator_id api/*current-user-id*
                           :timestamp  parsed}
                          (when-not icon
                            {:icon (t2/select-one-fn :icon Timeline :id timeline_id)}))]
      (snowplow/track-event! ::snowplow/timeline
                             (cond-> {:event         :new-event-created
                                      :time_matters  time_matters
                                      :collection_id (:collection_id timeline)}
                               (boolean source)      (assoc :source source)
                               (boolean question_id) (assoc :question_id question_id)))
      (first (t2/insert-returning-instances! TimelineEvent tl-event)))))

/:id

(api/defendpoint GET 
  "Fetch the [[TimelineEvent]] with `id`."
  [id]
  {id ms/PositiveInt}
  (api/read-check TimelineEvent id))

/:id

(api/defendpoint PUT 
  "Update a [[TimelineEvent]]."
  [id :as {{:keys [name description timestamp time_matters timezone icon timeline_id archived]
            :as   timeline-event-updates} :body}]
  {id           ms/PositiveInt
   name         [:maybe ms/NonBlankString]
   description  [:maybe :string]
   timestamp    [:maybe ms/TemporalString]
   time_matters [:maybe :boolean]
   timezone     [:maybe :string]
   icon         [:maybe timeline-event/Icon]
   timeline_id  [:maybe ms/PositiveInt]
   archived     [:maybe :boolean]}
  (let [existing (api/write-check TimelineEvent id)
        timeline-event-updates (cond-> timeline-event-updates
                                 (boolean timestamp) (update :timestamp u.date/parse))]
    (collection/check-allowed-to-change-collection existing timeline-event-updates)
    ;; todo: if we accept a new timestamp, must we require a timezone? gut says yes?
    (t2/update! TimelineEvent id
                (u/select-keys-when timeline-event-updates
                                    :present #{:description :timestamp :time_matters :timezone :icon :timeline_id :archived}
                                    :non-nil #{:name}))
    (t2/select-one TimelineEvent :id id)))

/:id

(api/defendpoint DELETE 
  "Delete a [[TimelineEvent]]."
  [id]
  {id ms/PositiveInt}
  (api/write-check TimelineEvent id)
  (t2/delete! TimelineEvent :id id)
  api/generic-204-no-content)
(api/define-routes)