(ns metabase.revisions.api
  (:require
   [metabase.api.common :as api]
   [metabase.api.macros :as api.macros]
   [metabase.models.card :as card]
   [metabase.revisions.models.revision :as revision]
   [metabase.util.malli.schema :as ms]
   [metabase.util.regex :as u.regex]
   [toucan2.core :as t2]))
(def ^:private entity->model
  {"card"      :model/Card
   "dashboard" :model/Dashboard
   "segment"   :model/Segment})

Schema for a valid revisionable entity name.

(def ^:private Entity
  (into
   [:enum {:api/regex (u.regex/re-or (keys entity->model))}]
   (keys entity->model)))
(defn- model-and-instance [entity-name id]
  (let [model (entity->model entity-name)]
    (assert (keyword? model))
    [model (t2/select-one model :id id)]))
(api.macros/defendpoint :get "/"
  "Get revisions of an object."
  [_route-params
   {:keys [entity id]} :- [:map
                           [:id     ms/PositiveInt]
                           [:entity Entity]]]
  (let [[model instance] (model-and-instance entity id)]
    (when (api/read-check instance)
      (revision/revisions+details model id))))
(api.macros/defendpoint :post "/revert"
  "Revert an object to a prior revision."
  [_route-params
   _query-params
   {:keys [entity id], revision-id :revision_id} :- [:map
                                                     [:id          ms/PositiveInt]
                                                     [:entity      Entity]
                                                     [:revision_id ms/PositiveInt]]]
  (let [[model instance] (model-and-instance entity id)
        _                (api/write-check instance)
        revision         (api/check-404 (t2/select-one :model/Revision :model (name model), :model_id id, :id revision-id))]
    ;; if reverting a Card, make sure we have *data* permissions to run the query we're reverting to
    (when (= model :model/Card)
      ;; TODO -- we should be using something like `api/read-check` for this, but unfortunately the impl for Cards
      ;; doesn't actually check important stuff like this.
      (card/check-run-permissions-for-query (get-in revision [:object :dataset_query])))
    ;; ok, we're g2g
    (revision/revert!
     {:entity      model
      :id          id
      :user-id     api/*current-user-id*
      :revision-id revision-id})))
(api.macros/defendpoint :get "/:entity/:id"
  "Fetch `Revisions` for an object with ID."
  [{:keys [id entity]} :- [:map
                           [:entity Entity]
                           [:id     ms/PositiveInt]]]
  (let [model (entity->model entity)]
    (assert (keyword? model))
    (api/read-check model id)
    (revision/revisions+details model id)))