(ns auto-ap.graphql.accounts
  (:require
   [auto-ap.datomic :refer [audit-transact conn]]
   [auto-ap.datomic.accounts :as d-accounts]
   [iol-ion.tx :refer [upsert-entity]]
   [auto-ap.graphql.utils
    :refer [->graphql
            <-graphql
            assert-admin
            assert-can-see-client
            cleanse-query
            enum->keyword
            is-admin?
            result->page]]
   [auto-ap.search :as search]
   [auto-ap.utils :refer [heartbeat]]
   [datomic.client.api :as dc]
   [mount.core :as mount]
   [yang.scheduler :as scheduler]))

(defn get-graphql [context args _]
  (assert-admin (:id context))
  (let [args                     (assoc args :id (:id context))
        [accounts accounts-count ] (d-accounts/get-graphql (<-graphql args))]
    (result->page accounts accounts-count :accounts args)))

(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)))))

(defn upsert-account [context args _]
  (let [{{:keys [id client-overrides numeric-code location applicability account-set name invoice-allowance vendor-allowance type]} :account} (<-graphql args)]
    (when-not id
      (when (seq (dc/q {:query {:find  ['?e]
                                :in    '[$ ?account-set ?numeric-code]
                                :where ['[?e :account/account-set ?account-set]
                                        '[?e :account/numeric-code ?numeric-code]]}
                        :args  [(dc/db conn) account-set numeric-code]}))

        (throw (ex-info (str "Account set " account-set " already has an account for code " numeric-code)
                        {} ))))
    (let [result (audit-transact [`(upsert-entity
                                     ~(cond-> {:db/id                    (or id "new-account")
                                               :account/name             name
                                               :account/search-terms     name
                                               :account/type             (keyword "account-type" (clojure.core/name type))
                                               :account/applicability    (or (enum->keyword applicability "account-applicability")
                                                                             :account-applicability/global)
                                               :account/invoice-allowance (some-> invoice-allowance (enum->keyword "allowance"))
                                               :account/vendor-allowance (some-> vendor-allowance (enum->keyword "allowance"))
                                               :account/default-allowance :allowance/allowed
                                               :account/account-set      account-set
                                               :account/location         location
                                               :account/numeric-code     numeric-code
                                               :account/code             (str numeric-code)
                                               :account/client-overrides (mapv
                                                                           (fn [client-override]
                                                                             {:db/id                                (:id client-override)
                                                                              :account-client-override/client       (:client-id client-override)
                                                                              :account-client-override/name         (:name client-override)
                                                                              :account-client-override/search-terms (:name client-override)})
                                                                           client-overrides)}
                                        id (dissoc :account/numeric-code :account/code)))]
                                 (:id context))]
      (->graphql 
        (d-accounts/get-by-id (or id (get-in result [:tempids "new-account"])))))))

(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 [context {query :query client :client_id allowance :allowance vendor-id :vendor_id} _]
  (when client
    (assert-can-see-client (:id context) client))
  (let [query (cleanse-query query)
        _ (println query)
        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))
        (->> (dc/q '[:find ?n (pull ?i pattern) ?s
                     :in $ [[?i ?n ?s]] pattern
                     :where (or [?i :account/applicability :account-applicability/global]
                                [?i :account/applicability :account-applicability/optional])]
                  (dc/db conn)
                  (search/search {:q query} "account")
                  search-pattern)
             (concat (when client
                       (dc/q '[:find ?n (pull ?i pattern) ?s
                              :in $ [[?i ?n ?s]] pattern]
                            (dc/db conn)
                            (search/search {:q query :client (str client)} "account-client-override")
                            search-pattern)))
             (sort-by (comp - last))
             (sequence xform)))
      [])))

(defn rebuild-search-index []
  (search/full-index-query  
    (for [result (map first (dc/qseq '[:find (pull ?aco [:account-client-override/search-terms :account-client-override/client :db/id {:account/_client-overrides [:account/numeric-code :account/location :db/id]}])
                                       :in $
                                       :where [?aco :account-client-override/client ]
                                       [?aco :account-client-override/search-terms ]
                                       [_ :account/client-overrides ?aco]]
                                     (dc/db conn)))
          :when (:account/numeric-code (:account/_client-overrides result))]
      {:id     (:db/id (:account/_client-overrides result))
       :account-client-override-id (:db/id result)
       :text   (:account-client-override/search-terms result)
       :client (str (:db/id (:account-client-override/client result)))
       :numeric-code (:account/numeric-code (:account/_client-overrides result))
       :location (:account/location (:account/_client-overrides result))})
    "account-client-override")

  (search/full-index-query  
    (for [result (map first (dc/qseq '[:find (pull ?a [:account/numeric-code
                                                       :account/search-terms
                                                       {:account/applicability [:db/ident]}
                                                       :db/id
                                                       :account/location])
                                       :in $
                                       :where [?a :account/search-terms ]]
                                     (dc/db conn)))
          :when (:account/search-terms result)
          ]
      {:id     (:db/id result)
       :text   (:account/search-terms result)
       :numeric-code (:account/numeric-code result)
       :location (:account/location result)
       :applicability (name (:db/ident (:account/applicability result)))})
    "account"))

#_(dc/transact conn
             {:tx-data [{:db/ident       :account-client-override/k2
                         :db/valueType   :db.type/tuple
                         :db/tupleAttrs  [:account/_client-overrides :account-client-override/client ]
                         :db/cardinality :db.cardinality/one
                         :db/unique      :db.unique/identity}]})

#_(dc/q '[:find ?o
        :where [_ :account-client-override/k ?o]]
      (dc/db conn))


#_(dc/pull (dc/db conn) '[*] [:db/ident :account-client-override/k])

#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(mount/defstate indexer
  :start (scheduler/every (* 5 60 1000) (heartbeat rebuild-search-index "rebuild-search-index"))
  :stop (scheduler/stop indexer))
