(ns auto-ap.datomic.sales-orders
  (:require
   [auto-ap.datomic
    :refer [add-sorter-fields-2
            apply-pagination
            apply-sort-3
            conn
            merge-query
            pull-id
            pull-many
            query2
            visible-clients]]
   [clj-time.coerce :as c]
   [clj-time.core :as time]
   [clojure.set :as set]
   [com.brunobonacci.mulog :as mu]
   [datomic.api :as dc]
   [iol-ion.query]))

(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 '[:db/id
                    :sales-order/external-id,
                    :sales-order/location,
                    :sales-order/date,
                    :sales-order/total,
                    :sales-order/tax,
                    :sales-order/tip,
                    :sales-order/line-items,
                    :sales-order/discount,
                    :sales-order/returns,
                    :sales-order/service-charge,
                    :sales-order/vendor,
                    :sales-order/source,
                    :sales-order/reference-link,
                    {:sales-order/client [:client/name :db/id :client/code]
                     :sales-order/charges [
                                           :charge/type-name,
                                           :charge/total,
                                           :charge/tax,
                                           :charge/tip,
                                           :charge/external-id,
                                           :charge/note,
                                           :charge/date,
                                           :charge/client,
                                           :charge/location,
                                           :charge/reference-link,
                                           {:charge/processor [:db/ident]} {:expected-deposit/_charges [:db/id]}]}])

(defn raw-graphql-ids [db args]
  (let [visible-clients  (set (map :db/id (:clients args)))
        selected-clients (->> (cond
                                (:client-id args)
                                (set/intersection #{(:client-id args)}
                                                  visible-clients)


                                (:client-code args)
                                (set/intersection #{(pull-id db [:client/code (:client-code args)])}
                                                  visible-clients)

                                :else
                                visible-clients)
                              (take 10)
                              set)
        _ (mu/log ::selected-clients
                  :selected-clients selected-clients)
        query (cond-> {:query {:find []
                               :in ['$ '[?clients ?start-date ?end-date]]
                               :where '[[(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]}
                       :args [db [selected-clients
                                  (some->  (:start (:date-range args)) c/to-date)
                                  (some-> (:end (:date-range args)) c/to-date )]]}

                (: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)
                (: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]
                                              '[(iol-ion.query/dollars= ?sales-order-total ?total)]]}
                              :args  [(:total args)]})

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

    (cond->> (query2 query)
      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 {:find  ['(sum ?t) '(sum ?tax)]
                             :with  ['?id]
                             :in    ['$ '[?id ...]]
                             :where ['[?id :sales-order/total ?t]
                                     '[?id :sales-order/tax ?tax]]}
                            (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} (mu/trace ::get-sales-order-ids [] (raw-graphql-ids db args))]
    [(->> (mu/trace ::get-results [] (graphql-results ids-to-retrieve db args)))
     matching-count
     (summarize-orders ids-to-retrieve)]))

(defn summarize-graphql [args]
  (let [db                                           (dc/db conn)
        {ids-to-retrieve :ids matching-count :count} (mu/trace ::get-sales-order-ids [] (raw-graphql-ids db args))]
    (summarize-orders ids-to-retrieve)))

