(ns metabase.indexed-entities.api
  (:require
   [metabase.analytics.core :as analytics]
   [metabase.api.common :as api]
   [metabase.api.macros :as api.macros]
   [metabase.indexed-entities.models.model-index :as model-index]
   [metabase.indexed-entities.task.index-values :as task.index-values]
   [metabase.legacy-mbql.normalize :as mbql.normalize]
   [metabase.util.i18n :refer [tru]]
   [metabase.util.malli.schema :as ms]
   [toucan2.core :as t2]))

Ensure that the ref exists and is of type required for indexing.

(defn- ensure-type
  [t ref metadata]
  (if-let [field (some (fn [f] (when ((comp #{(mbql.normalize/normalize-field-ref ref)} :field_ref) f)
                                 f))
                       metadata)]
    (let [type-slot (case t
                      :type/PK                   :semantic_type
                      (:type/Integer :type/Text) :effective_type)]
      (when-not (isa? (type-slot field) t)
        (throw (ex-info (tru "Field is not of {0} `{1}`" type-slot t)
                        {:status-code   400
                         :expected-type t
                         :type          (:effective_type field)
                         :field         (:name field)}))))
    (throw (ex-info (tru "Could not identify field by ref {0}" ref)
                    {:status-code 400
                     :ref         ref
                     :fields      metadata}))))
(api.macros/defendpoint :post "/"
  "Create ModelIndex."
  [_route-params
   _query-params
   {:keys [model_id pk_ref value_ref] :as _model-index} :- [:map
                                                            [:model_id  ms/PositiveInt]
                                                            [:pk_ref    any?]
                                                            [:value_ref any?]]]
  (let [model    (api/write-check :model/Card model_id)
        metadata (:result_metadata model)]
    (when-not (seq metadata)
      (throw (ex-info (tru "Model has no metadata. Cannot index")
                      {:model-id model_id})))
    (ensure-type :type/PK pk_ref metadata)
    (ensure-type :type/Integer pk_ref metadata)
    (ensure-type :type/Text value_ref metadata)
    ;; todo: do we care if there's already an index on that model?
    (let [model-index (model-index/create {:model-id   model_id
                                           :pk-ref     pk_ref
                                           :value-ref  value_ref
                                           :creator-id api/*current-user-id*})]
      (analytics/track-event! :snowplow/model
                              {:event    :index-model-entities-enabled
                               :model-id model_id})
      (task.index-values/add-indexing-job model-index)
      (model-index/add-values! model-index)
      (t2/select-one :model/ModelIndex :id (:id model-index)))))
(api.macros/defendpoint :get "/"
  "Retrieve list of ModelIndex."
  [_route-params
   {:keys [model_id]} :- [:map
                          [:model_id ms/PositiveInt]]]
  (let [model (api/read-check :model/Card model_id)]
    (when-not (= (:type model) :model)
      (throw (ex-info (tru "Question {0} is not a model" model_id)
                      {:model_id model_id
                       :status-code 400})))
    (t2/select :model/ModelIndex :model_id model_id)))
(api.macros/defendpoint :get "/:id"
  "Retrieve ModelIndex."
  [{:keys [id]} :- [:map
                    [:id ms/PositiveInt]]]
  (let [model-index (api/check-404 (t2/select-one :model/ModelIndex :id id))
        model       (api/read-check :model/Card (:model_id model-index))]
    (when-not (= (:type model) :model)
      (throw (ex-info (tru "Question {0} is not a model" id)
                      {:model_id id
                       :status-code 400})))
    model-index))
(api.macros/defendpoint :delete "/:id"
  "Delete ModelIndex."
  [{:keys [id]} :- [:map
                    [:id ms/PositiveInt]]]
  (api/let-404 [model-index (t2/select-one :model/ModelIndex :id id)]
    (api/write-check :model/Card (:model_id model-index))
    (t2/delete! :model/ModelIndex id)))