(ns auto-ap.ssr.account
  (:require
   [auto-ap.datomic :refer [conn]]
   [auto-ap.graphql.utils
    :refer [assert-can-see-client can-see-client? cleanse-query is-admin?]]
   [auto-ap.solr :as solr]
   [auto-ap.logging :as alog]
   [auto-ap.ssr.utils
    :refer [entity-id ref->enum-schema wrap-schema-enforce]]
   [com.brunobonacci.mulog :as mu]
   [datomic.api :as dc]
   [ring.middleware.json :refer [wrap-json-response]]
   [auto-ap.datomic.accounts :as d-accounts]))

;; TODO this is basically duplicative of graphql version, make sure to keep in sync
;; TODO use valid clients from request rather than stuff like assert-can-see-client

(def search-pattern [:db/id
                     :account/numeric-code
                     :account/location
                     :account/name
                     {:account/vendor-allowance  [:db/ident]
                      :account/default-allowance [:db/ident]
                      :account/invoice-allowance [:db/ident]
                      :account/client-overrides [:db/id
                                                 :account-client-override/name
                                                 {:account-client-override/client [:db/id :client/name]}]} ])

(defn search- [id query client]
  (let [client-part (if (some->> client (can-see-client? id))
                      (format "((applicability:(global OR optional) AND -client_id:*) OR (account_client_override_id:* AND client_id:%s))" client)
                      "(applicability:(global OR optional) AND -client_id:*)")
        query (format "_text_:(%s) AND %s" (cleanse-query query) client-part)]
    (mu/log ::searching :search-query query)
    (for [{:keys [account_id name] :as g} (solr/query solr/impl "accounts"
                                                      {"query" query
                                                       "fields" "id, name, client_id, numeric_code, applicability, account_id"})]

      {:account_id (first account_id)
       :name       (first name)})))

(defn account-search [{{:keys [q client-id purpose vendor-id] :as qp} :query-params id :identity :as request}]
  (let [client-id (or client-id (:db/id (:client request)))]
    (when client-id
      (assert-can-see-client id client-id))
    (let [num (some-> (re-find #"([0-9]+)" q)
                      second
                      (not-empty)
                      Integer/parseInt)

          valid-allowances (cond-> #{:allowance/allowed
                                     :allowance/warn}
                             (is-admin? id) (conj :allowance/admin-only))
          allowance (cond (= purpose "vendor")
                          :account/vendor-allowance
                          (= purpose "invoice")
                          :account/invoice-allowance
                          :else
                          :account/default-allowance)

          vendor-account (when vendor-id
                           (-> (dc/q '[:find ?da
                                       :in $ ?v
                                       :where [?v :vendor/default-account ?da]]
                                     (dc/db conn)
                                     vendor-id)
                               ffirst))
          xform (comp
                 (filter (fn [[_ a]]
                           (or
                            (valid-allowances (-> a allowance :db/ident))
                            (= (:db/id a) vendor-account))))
                 (map (fn [[n a]]
                        {:label     (str (:account/numeric-code a) " - " (if client-id 
                                                                           (:account/name (d-accounts/clientize a client-id))
                                                                             n))
                         :value       (:db/id a)
                         :location (:account/location a)
                         :warning  (when (= :allowance/warn (-> a allowance :db/ident))
                                     "This account is not typically used for this purpose.")})))]
      {:body (take 10 (if q
                        (if num
                          (->> (dc/q '[:find ?n (pull ?i pattern)
                                       :in $ ?numeric-code ?allowance pattern
                                       :where [?i :account/numeric-code ?numeric-code]
                                       [?i :account/name ?n]
                                       (or [?i :account/applicability :account-applicability/global]
                                           [?i :account/applicability :account-applicability/optional]
                                           [?i :account/applicability :account-applicability/customized])]
                                     (dc/db conn)
                                     num
                                     allowance
                                     search-pattern)
                               (sequence xform))
                          (->> (search- id q client-id)
                               (sequence
                                (comp (map (fn [i] [(:name i) (dc/pull (dc/db conn) search-pattern (:account_id i))]))
                                      xform))))
                        []))})))

(def account-search (wrap-json-response (wrap-schema-enforce  account-search
                                                              :query-schema [:map
                                                                             [:q :string]
                                                                             [:client-id {:optional true
                                                                                          :default nil}
                                                                              [:maybe entity-id]]
                                                                             [:vendor-id {:optional true}
                                                                              [:maybe entity-id]]
                                                                             [:purpose {:optional true}
                                                                              [:maybe :string]]])))

