(ns auto-ap.graphql.accounts
  (:require
   [auto-ap.datomic :refer [audit-transact conn]]
   [auto-ap.datomic.accounts :as d-accounts]
   [auto-ap.graphql.utils
    :refer [->graphql
            <-graphql
            assert-admin
            assert-can-see-client
            can-see-client?
            cleanse-query
            enum->keyword
            is-admin?
            limited-clients
            result->page]]
   [auto-ap.solr :as solr]
   [datomic.api :as dc]
   [iol-ion.tx :refer [random-tempid]]
   [com.brunobonacci.mulog :as mu]))


(defn get-all-graphql [context args _]
  (assert-admin (:id context))
  (let [args                     (assoc args :id (:id context))
        [accounts _] (d-accounts/get-graphql (assoc (<-graphql args) :per-page Integer/MAX_VALUE))]
    (map ->graphql accounts)))

(defn default-for-vendor [context args _]
  (assert-can-see-client (:id context) (:client_id args))
  (let [result (d-accounts/get-for-vendor (:vendor_id args) (:client_id args))]
    (->graphql  (d-accounts/clientize result (:client_id args)))))

(def search-pattern [:db/id
                     :account/numeric-code
                     :account/location
                     {:account/vendor-allowance  [:db/ident]
                      :account/default-allowance [:db/ident]
                      :account/invoice-allowance [:db/ident]}])
(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 search [context {query :query client :client_id allowance :allowance vendor-id :vendor_id} _]
  (when client
    (assert-can-see-client (:id context) client))
  (let [num (some-> (re-find #"([0-9]+)" query)
                    second
                    (not-empty)
                    Integer/parseInt)

        valid-allowances (cond-> #{:allowance/allowed
                                   :allowance/warn}
                           (is-admin? (:id context)) (conj :allowance/admin-only))
        allowance (cond (= allowance :vendor)
                        :account/vendor-allowance
                        (= allowance :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]]
                      {:name     (str (:account/numeric-code a) " - " n)
                       :id       (:db/id a)
                       :location (:account/location a)
                       :warning  (when (= :allowance/warn (-> a allowance :db/ident))
                                   "This account is not typically used for this purpose.")})))]
    (if query
      (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 context) query client)
             (sequence
              (comp (map (fn [i] [(:name i) (dc/pull (dc/db conn) search-pattern (:account_id i))]))
                    xform))))
      [])))

(defn rebuild-search-index []
  (solr/delete solr/impl "accounts")
  (Thread/sleep 10000)
  (solr/index-documents-raw
   solr/impl
   "accounts"
   (for [result (map first (dc/qseq {:query '[:find (pull ?aco [:account-client-override/search-terms :account-client-override/client :db/id {:account/_client-overrides [:account/numeric-code :account/location :db/id {:account/applicability [:db/ident]}]}])
                                              :in $
                                              :where [?aco :account-client-override/client]
                                              [?aco :account-client-override/search-terms]
                                              [_ :account/client-overrides ?aco]]
                                     :args  [(dc/db conn)]}))
         :when  (:account/numeric-code (:account/_client-overrides result))]
     {"id"                         (:db/id result)
      "account_id" (:db/id (:account/_client-overrides result))
      "account_client_override_id" (str (:db/id result))
      "name"                       (:account-client-override/search-terms result)
      "client_id"                  (str (:db/id (:account-client-override/client result)))
      "numeric_code"               (:account/numeric-code (:account/_client-overrides result))
      "location"                   (:account/location (:account/_client-overrides result))
      "applicability" (name (:db/ident (:account/applicability (:account/_client-overrides result))))}))
  (solr/index-documents-raw
   solr/impl
   "accounts"
   (for [result (map first (dc/qseq {:query '[:find (pull ?a [:account/numeric-code
                                                              :account/search-terms
                                                              {:account/applicability [:db/ident]}
                                                              :db/id
                                                              :account/location])
                                              :in $
                                              :where [?a :account/search-terms]]
                                     :args  [(dc/db conn)]}))
         :when  (:account/search-terms result)]
     {"id"            (:db/id result)
      "account_id"            (:db/id result)
      "name"          (:account/search-terms result)
      "numeric_code"  (:account/numeric-code result)
      "location"      (:account/location result)
      "applicability" (name (:db/ident (:account/applicability result)))})))
