(ns auto-ap.datomic.checks
  (:require [datomic.api :as d]
            [auto-ap.datomic :refer [uri merge-query apply-sort-3 apply-pagination add-sorter-fields conn]]
            [auto-ap.graphql.utils :refer [limited-clients]]
            [auto-ap.utils :refer [dollars=]]
            [clojure.set :refer [rename-keys]]
            [clj-time.coerce :as c]
            [clojure.tools.logging :as log]))

(defn <-datomic [result]
  (-> result
       (update :payment/date c/from-date)
       (update :payment/status :db/ident)
       (update :payment/type :db/ident)
       (update :transaction/_payment (fn [transactions]
                                       (mapv (fn [transaction]
                                               (update transaction :transaction/date c/from-date))
                                             transactions)))
       (rename-keys {:invoice-payment/_payment :payment/invoices})))

(def default-read '[*
                   {:invoice-payment/_payment [* {:invoice-payment/invoice [*]}]}
                   {:payment/client [:client/name :db/id :client/code]}
                   {:payment/bank-account [*]}
                   {:payment/vendor [:vendor/name {:vendor/default-account
                                                   [:account/name :account/numeric-code :db/id]} :db/id {:vendor/primary-contact [*]} {:vendor/address [*]}]}
                   {:payment/status [:db/ident]}
                    {:payment/type [:db/ident]}
                    {:transaction/_payment [:db/id :transaction/date]}])

(defn raw-graphql-ids [db args]
  (let [check-number-like (try (Long/parseLong (:check-number-like args)) (catch Exception e nil))
        query (cond-> {:query {:find []
                               :in ['$]
                               :where []}
                       :args [db]}
                (:sort args) (add-sorter-fields {"client" ['[?e :payment/client ?c]
                                                              '[?c :client/name ?sort-client]]
                                                    "vendor" ['[?e :payment/vendor ?v]
                                                              '[?v :vendor/name ?sort-vendor]] 
                                                    "bank-account" ['[?e :payment/bank-account ?ba]
                                                                    '[?ba :bank-account/name ?sort-bank-account]]
                                                    "check-number" ['[?e :payment/check-number ?sort-check-number]]
                                                    "date" ['[?e :payment/date ?sort-date]]
                                                    "amount" ['[?e :payment/amount ?sort-amount]]
                                                    "status" ['[?e :payment/status ?sort-status]]}
                                                  args)
                (:exact-match-id args)
                (merge-query {:query {:in ['?e]
                                      :where []}
                              :args [(:exact-match-id args)]})

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

                (:client-id args)
                (merge-query {:query {:in ['?client-id]
                                      :where ['[?e :payment/client ?client-id]]}
                              :args [(:client-id args)]})
                (:client-code args)
                (merge-query {:query {:in ['?client-code]
                                      :where ['[?e :payment/client ?client-id]
                                              '[?client-id :client/code ?client-code]]}
                              :args [(:client-code args)]})

                
                (:vendor-id args) 
                (merge-query {:query {:in ['?vendor-id]
                                      :where ['[?e :payment/vendor ?vendor-id]]}
                              :args [(:vendor-id args)]})

                (:original-id args)
                (merge-query {:query {:in ['?original-id]
                                      :where ['[?e :payment/client ?c]
                                              '[?c :client/original-id ?original-id]]}
                              :args [(:original-id args)]})

                (:check-number args)
                (merge-query {:query {:in ['?check-number]
                                      :where ['[?e :payment/check-number ?check-number]]}
                              :args [(:check-number args)]})

                (not-empty (:invoice-number args))
                (merge-query {:query {:in ['?invoice-number]
                                      :where ['[?e :payment/invoices ?i]
                                              '[?i :invoice/invoice-number ?invoice-number]]}
                              :args [(:invoice-number args)]})

                (:bank-account-id  args)
                (merge-query {:query {:in ['?bank-account-id]
                                      :where ['[?e :payment/bank-account ?bank-account-id]]}
                              :args [(:bank-account-id args)]})

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

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

                (:amount args)
                (merge-query {:query {:in ['?amount]
                                      :where ['[?e :payment/amount ?transaction-amount]
                                              '[(auto-ap.utils/dollars= ?transaction-amount ?amount)]]}
                              :args [(:amount args)]})

                
                (:status args)
                (merge-query {:query {:in ['?status]
                                      :where ['[?e :payment/status ?status]]}
                              :args [(:status args)]})
                
                (:start (:date-range args))
                (merge-query {:query {:in '[?start-date]
                                      :where ['[?e :payment/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 :payment/date ?date]
                                              '[(<= ?date ?end-date)]]}
                              :args [(c/to-date (:end (:date-range args)))]})

                (:payment-type args)
                (merge-query {:query {:in '[?payment-type]
                                      :where ['[?e :payment/type ?payment-type]]}
                              :args [(:payment-type args)]})

                check-number-like
                (merge-query {:query {:in '[?check-number-like]
                                      :where ['[?e :payment/check-number ?check-number-like]]}
                              :args [check-number-like]})

                true
                (merge-query {:query {:find ['?sort-default '?e]
                                      :where ['[?e :payment/date ?sort-default]]}}))]

    
    (log/info query)
    (cond->> query
      true (d/query)
      true (apply-sort-3 args)
      true (apply-pagination args))))

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

(defn get-graphql [args]
  (let [db (d/db (d/connect uri))
        {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]

    [(->> (graphql-results ids-to-retrieve db args))
     matching-count]))

(defn get-by-id [id]
  (->> 
   (d/pull (d/db (d/connect uri)) default-read id)
   (<-datomic)))
