(ns auto-ap.ssr.pos.cash-drawer-shifts
  (:require
   [auto-ap.datomic
    :refer [add-sorter-fields apply-pagination apply-sort-3 conn merge-query
            pull-many query2]]
   [auto-ap.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* total-field*]]
   [auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers clj-date-schema
                              default-grid-fields-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]))

;; always should be fast
(def query-schema (mc/schema
                   [:maybe
                    (into [:map {:date-range [:date-range :start-date :end-date]}
                           [: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]}]]] ]
                          default-grid-fields-schema)]))

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

   [:fieldset.space-y-6 
    (date-range-field* params)
    (total-field* params)]])

(def default-read '[:db/id
                    :cash-drawer-shift/paid-in :cash-drawer-shift/paid-out :cash-drawer-shift/expected-cash :cash-drawer-shift/opened-cash
                    [:cash-drawer-shift/date :xform clj-time.coerce/from-date]
                    {:cash-drawer-shift/client [:client/name :db/id :client/code]}])

(defn fetch-ids [db request]
  (let [query-params (:query-params request)
        query (cond-> {:query {:find  []
                               :in    ['$ '[?clients ?start-date ?end-date]]
                               :where '[[(iol-ion.query/scan-cash-drawer-shifts $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]}
                       :args  [db [(:trimmed-clients request)
                                   (some->  (:start-date query-params) c/to-date)
                                   (some-> (:end-date query-params) c/to-date )]]}
                (:sort query-params) (add-sorter-fields {"client" ['[?e :cash-drawer-shift/client ?c]
                                                                   '[?c :client/name ?sort-client]]
                                                         "date" ['[?e :cash-drawer-shift/date ?sort-date]]
                                                         "paid-in" ['[?e :cash-drawer-shift/paid-in ?sort-paid-in]]
                                                         "paid-out" ['[?e :cash-drawer-shift/paid-out ?sort-paid-out]]
                                                         "expected-cash" ['[?e :cash-drawer-shift/expected-cash ?sort-expected-cash]]
                                                         "opened-cash" ['[?e :cash-drawer-shift/opened-cash ?sort-opened-cash]]
                                                         }
                                                        query-params)

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

                true
                (merge-query {:query {:find ['?sort-default '?e]
                                      :where ['[?e :cash-drawer-shift/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))
        cash-drawer-shifts (->> ids
                     (map results)
                     (map first))]
    cash-drawer-shifts))

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

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

(def grid-page
  (helper/build
   {:id                "cash-drawer-shift-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-cash-drawer-shifts)}
                         "Cash Drawer Shifts"]]
    :title          "Cash drawer shifts"
    :entity-name    "Cash drawer shift"
                 :query-schema query-schema
    :route          :pos-cash-drawer-shift-table
    :headers        [{:key      "client"
                      :name     "Client"
                      :sort-key "client"
                      :hide?    (fn [args]
                                  (= (count (:clients args)) 1))
                      :render   #(-> % :cash-drawer-shift/client :client/code)}
                     {:key      "date"
                      :name     "Date"
                      :sort-key "date"
                      :render   #(atime/unparse-local (:cash-drawer-shift/date %) atime/standard-time)}
                     {:key      "paid-in"
                      :name     "Paid in"
                      :sort-key "paid-in"
                      :render   #(some->> % :cash-drawer-shift/paid-in (format "$%.2f"))}
                     {:key      "paid-out"
                      :name     "Paid out"
                      :sort-key "paid-out"
                      :render   #(some->> % :cash-drawer-shift/paid-out (format "$%.2f"))}
                     {:key      "expected-cash"
                      :name     "Expected cash"
                      :sort-key "expected-cash"
                      :render   #(some->> % :cash-drawer-shift/expected-cash (format "$%.2f"))}
                     {:key      "opened-cash"
                      :name     "Opened cash"
                      :sort-key "opened-cash"
                      :render   #(some->> % :cash-drawer-shift/opened-cash (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-cash-drawer-shifts      (helper/page-route grid-page)
    :pos-cash-drawer-shift-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)))))
