(ns auto-ap.datomic.sales-orders
  (:require
   [auto-ap.datomic
    :refer [add-sorter-fields-2
            apply-pagination
            apply-sort-3
            merge-query
            pull-many
            conn]]
   [auto-ap.graphql.utils :refer [limited-clients]]
   [clj-time.coerce :as c]
   [clojure.set :as set]
   [clojure.tools.logging :as log]
   [datomic.client.api :as dc]
   [com.brunobonacci.mulog :as mu]
   ))

(defn <-datomic [result]
  (-> result
      (update :sales-order/date c/from-date)
      (update :sales-order/charges (fn [cs]
                                     (map (fn [c]
                                            (-> c
                                              (update :charge/processor :db/ident)
                                              (set/rename-keys {:expected-deposit/_charges :expected-deposit})
                                              (update :expected-deposit first)))
                                          cs)))))

(def default-read '[*
                    {:sales-order/client [:client/name :db/id :client/code]
                     :sales-order/charges [* {:charge/processor [:db/ident]} {:expected-deposit/_charges [:db/id]}]}])

#_(defn client-index-search [db attr client-ids [start end]]
  (for [c client-ids
        d
        (dc/q '[:find ?e
                :in $ ?start ?end
                :where [?e :sales-order/client+date ?g]
                [(>= ?g ?start)]
                [(<= ?g ?end)]]
              db [c start] [c end])

        #_(dc/index-range db {:attrid attr
                              :start  [c start]
                              :end    [c end]})]
    (first d)))

#_(comment 
  (do 
    (println "starting")
    (doseq [n (partition-all 500 (dc/q '[:find ?s ?d
                                          :where [?s :sales-order/date ?d]]
                                        (dc/db conn)))]
      (print ".")
      (dc/transact conn {:tx-data (map (fn [[n d]] {:db/id n :sales-order/date d}) n)}))
    (println "done"))


  (time (count (client-index-search (dc/db conn)
                                    :sales-order/client+date
                                    [3575611821801781]
                                    [#inst "2021-05-21T18:06:02.000-00:00"
                                     #inst "2022-09-25T18:06:02.000-00:00"]
                                    )))

  (time (count (dc/q '[:find ?e
                       :in $ ?client ?start ?end
                       :where [?e :sales-order/client ?client]
                       [?e :sales-order/date ?date]
                       [(>= ?date ?start)]
                       [(<= ?date ?end)]]
                     (dc/db conn)
                      3575611821801781

                      #inst "2021-05-21T18:06:02.000-00:00"
                      #inst "2022-09-25T18:06:02.000-00:00"
                      )))

  (time (do (count (dc/q '[:find  ?e,
                           :in    $ [[?s1 ?e1]]
                           :where
                           #_[(untuple ?period) [?s1 ?e1]]
                           [(q '[:find ?e
                                 :in $ ?s1 ?e1
                                 :where [?e :sales-order/client+date ?g]
                                 [(>= ?g ?s1)]
                                 [(<= ?g ?e1)]]
                               $ ?s1 ?e1) [?e ...]]
                           ]
                         (dc/db conn) [[[3575611821801781 #inst "2022-08-21T18:06:02.000-00:00" ] [3575611821801781 #inst "2022-09-25T18:06:02.000-00:00"]]
                                       [[3575611821801766 #inst "2022-08-21T18:06:02.000-00:00" ] [3575611821801766 #inst "2022-09-25T18:06:02.000-00:00"]]]))  
              (println "done"))) 

  (dc/index-range (dc/db conn) {:attrid :sales-order/date+client3
                                :start  [#inst "2022-08-25T18:06:02.000-00:00" ]
                                :end    [#inst "2022-08-25T20:06:02.000-00:00" ]}) 
  (dc/pull (dc/db conn) '[*] :sales-order/date+client3)
  (dc/pull (dc/db conn) '[*] 5040161313201309) 

  #_(time (do (dc/q {:query '{:find  [(count ?x)],
                              :in    [$ ?client-id],
                              :where [[?e :sales-order/date+client3 ?x]]},
                     :args  [(dc/db conn) 3575611821801781]}) 
              (println "done")))

  #_(datomic.dev-local.async/return-1)

  (clojure.core.async/<!! (dca/transact conn {:tx-data [{:db/ident       :sales-order/client+date
                                                           :db/valueType   :db.type/tuple
                                                           :db/tupleAttrs  [:sales-order/client :sales-order/date]
                                                           :db/cardinality :db.cardinality/one}]
                                                :timeout 600000}))


  )

(defn raw-graphql-ids [db args]
  (let [query (cond-> {:query {:find []
                               :in ['$]
                               :where []}
                       :args [db]}
                (:sort args) (add-sorter-fields-2 {"client" ['[?e :sales-order/client ?c]
                                                           '[?c :client/name ?sort-client]]
                                                 "location" ['[?e :sales-order/location ?sort-location]]
                                                 "source" ['[?e :sales-order/source ?sort-source]]
                                                 "date" ['[?e :sales-order/date ?sort-date]]
                                                 "total" ['[?e :sales-order/total ?sort-total]]
                                                 "tax" ['[?e :sales-order/tax ?sort-tax]]
                                                 "tip" ['[?e :sales-order/tip ?sort-tip]]}
                                                  args)

                (:start (:date-range args))
                (merge-query {:query {:in    '[?start-date]
                                      :where ['[?e :sales-order/date ?date]
                                              '[(>= ?date ?start-date)]]}
                              :args  [(c/to-date (:start (:date-range args)))]})

                (:end (:date-range args))
                (merge-query {:query {:in    '[?end-date]
                                      :where ['[?e :sales-order/date ?date]
                                              '[(<= ?date ?end-date)]]}
                              :args  [(c/to-date (:end (:date-range args)))]})

                (limited-clients (:id args))
                (merge-query {:query {:in ['[?xx ...]]
                                      :where ['[?e :sales-order/client ?xx]]}
                              :args [(set (map :db/id (limited-clients (:id args))))]})

                (:client-id args)
                (merge-query {:query {:in ['?client-id]
                                      :where ['[?e :sales-order/client ?client-id]]}
                              :args [(:client-id args)]})
                (:client-code args)
                (merge-query {:query {:in ['?client-code]
                                      :where ['[?e :sales-order/client ?client-id]
                                              '[?client-id :client/code ?client-code]]}
                              :args [(:client-code args)]})
                
                (:category args)
                (merge-query {:query {:in    ['?category]
                                      :where ['[?e :sales-order/line-items ?li]
                                              '[?li :order-line-item/category ?category]]}
                              :args  [(:category args)]})

                (:processor args)
                (merge-query {:query {:in ['?processor]
                                      :where ['[?e :sales-order/charges ?chg]
                                              '[?chg :charge/processor ?processor]]}
                              :args [(keyword "ccp-processor"
                                              (name (:processor args)))]})
                (:type-name args)
                (merge-query {:query {:in    ['?type-name]
                                      :where ['[?e :sales-order/charges ?chg]
                                              '[?chg :charge/type-name ?type-name]]}
                              :args  [(:type-name args)]})

                (:total-gte args) 
                (merge-query {:query {:in ['?total-gte]
                                      :where ['[?e :sales-order/total ?a]
                                              '[(>= ?a ?total-gte)]]}
                              :args [(:total-gte args)]})

                (:total-lte args) 
                (merge-query {:query {:in ['?total-lte]
                                      :where ['[?e :sales-order/total ?a]
                                              '[(<= ?a ?total-lte)]]}
                              :args [(:total-lte args)]})

                (:total args)
                (merge-query {:query {:in ['?total]
                                      :where ['[?e :sales-order/total ?sales-order-total]
                                              '[(auto-ap.utils/dollars= ?sales-order-total ?total)]]}
                              :args [(:total args)]})

                true
                (merge-query {:query {:find ['?date '?e]
                                      :where ['[?e :sales-order/date ?date]]}}))]

    (println query)
    (mu/log ::query-stats
            :stats (:query-stats (dc/q (assoc query :query-stats true)))
            :q (str query))

    (cond->> query
      true (dc/q)
      true (apply-sort-3 (assoc args :default-asc? false))
      true (apply-pagination args))))

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

(defn summarize-orders [ids]

  (let [[total tax] (->>
         (dc/q {:query {:find ['(sum ?t) '(sum ?tax)]
                           :with ['?id]
                           :in ['$ '[?id ...]]
                           :where ['[?id :sales-order/total ?t]
                                   '[?id :sales-order/tax ?tax]]}
                   :args [(dc/db conn)
                          ids]})
         first)]
    {:total total
     :tax tax}))

(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
     (summarize-orders ids-to-retrieve)]))

