(ns auto-ap.datomic.accounts
  (:require
   [auto-ap.datomic
    :refer [add-sorter-fields
            apply-pagination
            apply-sort-3
            conn
            merge-query
            pull-many
            query2]]
   [clojure.string :as str]
   [datomic.api :as dc]))

(defn <-datomic [a]
  (-> a 
      (update  :account/applicability :db/ident)
      (update  :account/invoice-allowance :db/ident)
      (update  :account/vendor-allowance :db/ident)))

(def default-read ['* {:account/type             [:db/ident :db/id]
                      :account/applicability    [:db/ident :db/id]
                       :account/invoice-allowance    [:db/ident :db/id]
                       :account/vendor-allowance    [:db/ident :db/id]
                      :account/client-overrides [:db/id
                                                 :account-client-override/name
                                                 {:account-client-override/client [:db/id :client/name]}]}])

(defn clientize [a client]
  (if-let [override-name (->> a
                              :account/client-overrides
                              (filter #(= client
                                          (:db/id (:account-client-override/client %))))
                              first
                              :account-client-override/name)]
    (assoc a :account/name override-name)
    a))

(defn get-accounts
  ([]
   (get-accounts {}))
  ([args]
   (let [query (cond-> {:query {:find [(list 'pull '?e default-read)]
                                :in ['$]
                                :where [['?e :account/name]]}
                        :args [(dc/db conn)]}
                 (:account-set args) (merge-query {:query {:in ['?account-set]
                                                           :where [['?e :account/account-set '?account-set]]}
                                                   :args [(:account-set args)]}))]
     (->>
      (query2 query)
      (map first)
      (map <-datomic)))))

(defn get-for-vendor [vendor-id client-id]
  (if client-id 
    (->>
      (dc/q '[:find  (pull ?e r)
              :in    $ ?v ?c r
              :where (or-join [?v ?c ?e]
                              (and [?v :vendor/account-overrides ?ao]
                                   [?ao :vendor-account-override/client ?c]
                                   [?ao :vendor-account-override/account ?e])
                              (and [?v :vendor/account-overrides ?ao]
                                   (not [?ao :vendor-account-override/client ?c])
                                   [?v :vendor/default-account ?e])
                              (and (not [?v :vendor/account-overrides])
                                   [?v :vendor/default-account ?e]))]

           (dc/db conn )
           vendor-id
           client-id
           default-read)
      (map first)
      (map <-datomic)
      first)
    (<-datomic (dc/q '[:find  (pull ?e r)
                       :in    $ ?v r
                       :where [?v :vendor/default-account ?e]]

                     (dc/db conn )
                     vendor-id
                     default-read))))

(defn get-account-by-numeric-code-and-sets [numeric-code _]
  (->>
    (dc/q {:find  ['(pull ?e [* {:account/type [:db/ident :db/id]}])]
           :in    ['$ '?numeric-code]
           :where ['[?e :account/numeric-code ?numeric-code]]}
          (dc/db conn) numeric-code)
    (map first)
    (map <-datomic)
    (first)))

(defn raw-graphql-ids [db args]
  (let [query (cond-> {:query {:find []
                               :in ['$]
                               :where []}
                       :args [db]}
                (:sort args) (add-sorter-fields {"name" ['[?e :account/name ?sort-name]]}
                                                args)

                (:numeric-code args)
                (merge-query {:query {:in    ['?numeric-code]
                                      :where ['[?e :account/numeric-code ?numeric-code]]}
                              :args  [(:numeric-code args)]})

                (not (str/blank? (:name-like args)))
                (merge-query {:query {:in    ['?name-like]
                                      :where ['[?e :account/name ?n]
                                              '[(re-find ?name-like ?n)]]}
                              :args  [(re-pattern (str "(?i)" (:name-like args)))]})

                true
                (merge-query {:query {:find  ['?sort-default '?e ]
                                      :where ['[?e :account/name]
                                              '[?e :account/numeric-code ?sort-default]]}}))]

    
    (cond->> (query2 query)
      true (apply-sort-3 args)
      true (apply-pagination args))))


(defn graphql-results [ids db _]
  (let [results (->> (pull-many db default-read ids)
                     (group-by :db/id))
        accounts (->> ids
                     (map results)
                     (map first)
                     (map <-datomic))]
    accounts))

(defn get-graphql [args]
  (let [db                                           (dc/db conn)
        {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]
    [(->> (graphql-results ids-to-retrieve db args))
     matching-count]))
