(ns auto-ap.ssr.pos.expected-deposits
  (:require
   [auto-ap.client-routes :as client-routes]
   [auto-ap.datomic
    :refer [add-sorter-fields apply-pagination apply-sort-3 conn merge-query
            pull-many query2]]
   [auto-ap.graphql.utils :refer [extract-client-ids]]
   [auto-ap.query-params :as query-params :refer [wrap-copy-qp-pqp]]
   [auto-ap.ssr-routes :as ssr-routes]
   [auto-ap.ssr.components :as com]
   [auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
   [auto-ap.ssr.pos.common :refer [date-range-field* exact-match-id-field*]]
   [auto-ap.ssr.svg :as svg]
   [auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers clj-date-schema
                              default-grid-fields-schema ref->enum-schema
                              wrap-merge-prior-hx wrap-schema-enforce]]
   [auto-ap.time :as atime]
   [bidi.bidi :as bidi]
   [clj-time.coerce :as c]
   [datomic.api :as dc]
   [malli.core :as mc]))

(def query-schema (mc/schema
                   [:maybe
                    (into [:map {:date-range [:date-range :start-date :end-date]}
                           [:total-gte {:optional true} [:maybe :double]]
                           [:total-lte {:optional true} [:maybe :double]]
                           [:start-date {:optional true}
                            [:maybe clj-date-schema]]
                           [:end-date {:optional true}
                            [:maybe clj-date-schema]]
                           [:client {:optional true :default nil} [:maybe  [:entity-map {:pull [:db/id :client/name]}]]]
                           [:processor {:optional true} [:maybe (ref->enum-schema "ccp-processor")]]]
                          default-grid-fields-schema)]))


(defn filters [request]
  [:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
          "hx-get" (bidi/path-for ssr-routes/only-routes
                                  :pos-expected-deposit-table)
          "hx-target" "#expected-deposit-table"
          "hx-indicator" "#expected-deposit-table"
          #_#_:hx-disabled-elt "find fieldset"}

   [:fieldset.space-y-6 
    (date-range-field* request)
    (exact-match-id-field* request)]])

(def default-read '[:db/id
                    :expected-deposit/location
                    :expected-deposit/total
                    :expected-deposit/fee
                    [:expected-deposit/date :xform clj-time.coerce/from-date]
                    {:expected-deposit/client [:client/name :db/id :client/code]
                     [:expected-deposit/status :xform iol-ion.query/ident] [:db/ident]
                     :transaction/_expected-deposit [:transaction/date :db/id]}])

(defn fetch-ids [db request]

  (let [query-params (:query-params request)
        valid-clients (extract-client-ids (:clients request)
                                          (:client request)
                                          (:client-id query-params)
                                          (when (:client-code query-params)
                                            [:client/code (:client-code query-params)]))
        query (cond-> {:query {:find  []
                               :in    ['$ '[?clients ?start-date ?end-date]]
                               :where '[[(iol-ion.query/scan-expected-deposits $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]}
                       :args  [db [valid-clients
                                   (some-> (:start-date query-params) c/to-date)
                                   (some-> (:end-date query-params) c/to-date)]]}
                (:sort query-params) (add-sorter-fields {"client" ['[?e :expected-deposit/client ?c]
                                                           '[?c :client/name ?sort-client]]
                                                 "location" ['[?e :expected-deposit/location ?sort-location]]
                                                 "date" ['[?e :expected-deposit/date ?sort-date]]
                                                 "total" ['[?e :expected-deposit/total ?sort-total]]
                                                 "fee" ['[?e :expected-deposit/fee ?sort-fee]]}
                                                  query-params)

                (:exact-match-id query-params)
                (merge-query {:query {:in ['?e]
                                      :where []}
                              :args [(:exact-match-id query-params)]})


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

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

                (:total query-params)
                (merge-query {:query {:in ['?total]
                                      :where ['[?e :expected-deposit/total ?expected-deposit-total]
                                              '[(iol-ion.query/dollars= ?expected-deposit-total ?total)]]}
                              :args [(:total query-params)]})

                true
                (merge-query {:query {:find ['?sort-default '?e]
                                      :where ['[?e :expected-deposit/date ?sort-default]]}}))]
    
    (cond->> (query2 query)
      true (apply-sort-3 query-params)
      true (apply-pagination query-params))))

(defn hydrate-results [ids db _]
  (let [results (->> (pull-many db default-read ids)
                     (group-by :db/id))
        payments (->> ids
                     (map results)
                     (map first)
                     (map (fn get-totals [ed]
                            (assoc ed :totals
                                   (->> (dc/q '[:find ?d4 (count ?c) (sum ?a)
                                               :in $ ?ed
                                               :where [?ed :expected-deposit/charges ?c]
                                               [?c :charge/total ?a]
                                               [?o :sales-order/charges ?c]
                                               [?o :sales-order/date ?d]
                                               [(clj-time.coerce/from-date ?d) ?d2]
                                               [(auto-ap.time/localize ?d2) ?d3]
                                               [(clj-time.coerce/to-local-date ?d3) ?d4]]
                                             (dc/db conn)
                                             (:db/id ed))
                                        (map (fn [[date count amount]]
                                               {:date (c/to-date-time date)
                                                :count count
                                                :amount amount})))))))]
    payments))

(defn fetch-page [args]
  (let [db (dc/db conn)
        {ids-to-retrieve :ids matching-count :count} (fetch-ids db args)]

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

(def grid-page
  (helper/build 
    {:id                 "expected-deposit-table"
     :nav                com/main-aside-nav
     :page-specific-nav  filters
     :fetch-page         fetch-page
     :oob-render
     (fn [request]
       [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
     :breadcrumbs        [[:a {:href (bidi/path-for ssr-routes/only-routes
                                                    :company)}
                           "POS"]

                          [:a {:href (bidi/path-for ssr-routes/only-routes
                                                    :pos-expected-deposits)}
                           "Expected deposits"]]
     :title          "Expected deposits"
     :entity-name    "Expected deposit"
                 :query-schema query-schema
     :route          :pos-expected-deposit-table
     :row-buttons    (fn [_ e]
                       [
                        (when (:expected-deposit/reference-link e)
                          (com/a-icon-button {:href (:expected-deposit/reference-link e)}
                                             svg/external-link))
                        (when-let [transaction-id (-> e (:transaction/_expected-deposit) first :db/id)]
                          (com/a-button {:href (str (bidi/path-for client-routes/routes
                                                                   :transactions)
                                                    "?exact-match-id="
                                                    transaction-id)} "Transaction"))])
     :headers        [{:key      "client"
                       :name     "Client"
                       :sort-key "client"
                       :hide?    (fn [args]
                                   (= (count (:clients args)) 1))
                       :render   #(-> % :expected-deposit/client :client/code)}
                      {:key      "date"
                       :name     "Date"
                       :sort-key "date"
                       :render   #(atime/unparse-local (:expected-deposit/date %) atime/standard-time)}
                      {:key      "sales-date"
                       :name     "Sales Date"
                       :sort-key "sales-date"
                       :render   #(atime/unparse-local (:expected-deposit/sales-date %) atime/standard-time)}
                      {:key      "total"
                       :name     "Total"
                       :sort-key "total"
                       :render   #(some->> % :expected-deposit/total (format "$%.2f"))}
                      {:key      "fee"
                       :name     "Fee"
                       :sort-key "fee"
                       :render   #(some->> % :expected-deposit/fee (format "$%.2f"))}]}))

(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))


(def key->handler
  (apply-middleware-to-all-handlers
   {:pos-expected-deposits      (helper/page-route grid-page)
   :pos-expected-deposit-table (helper/table-route grid-page)}
   (fn [h]
     (-> h
         (wrap-copy-qp-pqp)
         (wrap-apply-sort grid-page)
         (wrap-merge-prior-hx)
         (wrap-schema-enforce :query-schema query-schema)
         (wrap-schema-enforce :hx-schema query-schema)))))
