(ns auto-ap.ssr.payments
  (:require [auto-ap.client-routes :as client-routes]
            [auto-ap.datomic
             :refer [add-sorter-fields apply-pagination apply-sort-3
                     audit-transact conn merge-query observable-query
                     pull-many]]
            [auto-ap.graphql.utils :refer [assert-can-see-client
                                           exception->notification
                                           extract-client-ids notify-if-locked]]
            [auto-ap.query-params :refer [wrap-copy-qp-pqp]]
            [auto-ap.logging :as alog]
            [auto-ap.permissions :refer [can?]]
            [auto-ap.routes.invoice :as invoice-route]
            [auto-ap.routes.payments :as route]
            [auto-ap.routes.utils
             :refer [wrap-admin wrap-client-redirect-unauthenticated]]
            [auto-ap.ssr-routes :as ssr-routes]
            [auto-ap.ssr.components :as com]
            [auto-ap.ssr.components.bank-account-icon :as bank-account-icon]
            [auto-ap.ssr.components.link-dropdown :refer [link-dropdown]]
            [auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
            [auto-ap.ssr.hx :as hx]
            [auto-ap.ssr.pos.common :refer [date-range-field*]]
            [auto-ap.ssr.svg :as svg]
            [auto-ap.ssr.utils
             :refer [apply-middleware-to-all-handlers clj-date-schema
                     dissoc-nil-transformer entity-id html-response
                     main-transformer modal-response ref->enum-schema strip
                     wrap-entity wrap-implied-route-param wrap-merge-prior-hx
                     wrap-schema-enforce]]
            [auto-ap.time :as atime]
            [bidi.bidi :as bidi]
            [clj-time.coerce :as coerce]
            [clojure.string :as str]
            [datomic.api :as dc]
            [hiccup.util :as hu]
            [iol-ion.query :refer [dollars-0?]]
            [malli.core :as mc]
            [malli.transform :as mt]))

(defn exact-match-id* [request]
  (if (nat-int? (:exact-match-id (:query-params request)))
    [:div {:x-data (hx/json {:exact_match (:exact-match-id (:query-params request))}) :id "exact-match-id-tag"}
     (com/hidden {:name "exact-match-id"
                  "x-model" "exact_match"})
     (com/pill {:color :primary}
               [:span.inline-flex.space-x-2.items-center
                [:div "exact match"]
                [:div.w-3.h-3
                 (com/link {"@click" "exact_match=null; $nextTick(() => $dispatch('change'))"}
                           svg/x)]])]
    [:div {:id "exact-match-id-tag"}]))

(defn filters [request]
  [:form#payment-filters {"hx-trigger"   "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
                          "hx-get"       (bidi/path-for ssr-routes/only-routes
                                                        ::route/table)
                          "hx-target"    "#entity-table"
                          "hx-indicator" "#entity-table"}

   (com/hidden {:name "status"
                :value (some-> (:status (:query-params request)) name)})
   [:fieldset.space-y-6
    (com/field {:label "Vendor"}
               (com/typeahead {:name        "vendor"
                               :id          "vendor"
                               :url (bidi/path-for ssr-routes/only-routes :vendor-search)
                               :value       (:vendor (:query-params request))
                               :value-fn :db/id
                               :content-fn :vendor/name}))
    (date-range-field* request)
    (com/field {:label "Check #"}
               (com/text-input {:name        "check-number"
                                :id          "check-number"
                                :class       "hot-filter"
                                :value       (:check-number (:query-params request))
                                :placeholder "10001"
                                :size        :small}))
    (com/field {:label "Invoice #"}
               (com/text-input {:name        "invoice-number"
                                :id          "invoice-number"
                                :class       "hot-filter"
                                :value       (:invoice-number (:query-params request))
                                :placeholder "10001"
                                :size        :small}))

    (com/field {:label "Amount"}
               [:div.flex.space-x-4.items-baseline
                (com/money-input {:name        "amount-gte"
                                  :id          "amount-gte"
                                  :hx-preserve "true"
                                  :class       "hot-filter  w-20"
                                  :value       (:amount-gte (:query-params request))
                                  :placeholder "0.01"
                                  :size        :small})
                [:div.align-baseline
                 "to"]
                (com/money-input {:name        "amount-lte"
                                  :hx-preserve "true"
                                  :id          "amount-lte"
                                  :class       "hot-filter w-20"
                                  :value       (:amount-lte (:query-params request))
                                  :placeholder "9999.34"
                                  :size        :small})])
    (com/field {:label "Payment Type"}
               (com/radio-card {:size    :small
                           :name    "payment-type"
                           :value (:payment-type (:query-params request))
                           :options [{:value   ""
                                      :content "All"}
                                     {:value   "cash"
                                      :content "Cash"}
                                     {:value   "check"
                                      :content "Check"}
                                     {:value   "debit"
                                      :content "Debit"}]}))
    (exact-match-id* request)]])


(def default-read '[*
                    [:payment/date :xform clj-time.coerce/from-date]
                    {:invoice-payment/_payment [* {:invoice-payment/invoice [*]}]}
                    {:payment/client [:client/name :db/id :client/code]}
                    {:payment/bank-account [* {[:bank-account/type :xform iol-ion.query/ident] [:db/ident]}]}
                    {:payment/invoices [:db/id :invoice/invoice-number]}
                    {:payment/vendor [:vendor/name {:vendor/default-account
                                                    [:account/name :account/numeric-code :db/id]} :db/id {:vendor/primary-contact [*]} {:vendor/address [*]}]}
                    {[:payment/status :xform iol-ion.query/ident] [:db/ident]}
                    {[:payment/type :xform iol-ion.query/ident] [:db/ident]}
                    {:transaction/_payment [:db/id :transaction/date]}])

(defn fetch-ids [db {:keys [query-params route-params] :as request}]
  (let [valid-clients (extract-client-ids (:clients request)
                                          (:client request)
                                          (:client-id query-params)
                                          (when (:client-code query-params)
                                            [:client/code (:client-code query-params)]))

        check-number-like (try (Long/parseLong (:check-number query-params)) (catch Exception _ nil))
        query             (if (:exact-match-id query-params)
                            {:query {:find  '[?e]
                                     :in    '[$ ?e [?c ...]]
                                     :where '[[?e :payment/client ?c]]}
                             :args  [db
                                     (:exact-match-id query-params)
                                     valid-clients]}
                            (cond-> {:query {:find []
                                             :in '[$ [?clients ?start ?end]]
                                             :where '[[(iol-ion.query/scan-payments $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]]}
                                     :args [db
                                            [valid-clients
                                             (some-> (:start-date query-params) coerce/to-date)
                                             (some-> (:end-date query-params) coerce/to-date)]]}
                              (:sort query-params) (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" ['[(get-else $ ?e :payment/check-number 0) ?sort-check-number]]
                                                                       "date"         ['[?e :payment/date ?sort-date]]
                                                                       "amount"       ['[?e :payment/amount ?sort-amount]]
                                                                       "status"       ['[?e :payment/status ?sort-status]]}
                                                                      query-params)
                              (:exact-match-id query-params)
                              (merge-query {:query {:in    ['?e]
                                                    :where []}
                                            :args  [(:exact-match-id query-params)]})

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

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

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

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

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

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

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

                              (:amount query-params)
                              (merge-query {:query {:in    ['?amount]
                                                    :where ['[?e :payment/amount ?transaction-amount]
                                                            '[(iol-ion.query/dollars= ?transaction-amount ?amount)]]}
                                            :args  [(:amount query-params)]})


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

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

                              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]}})))]
    (cond->> (observable-query 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))
        refunds (->> ids
                     (map results)
                     (map first))]
    refunds))

(defn sum-visible-pending [ids]
  (->> 
    (dc/q {:find  ['?id '?o]
           :in    ['$ '[?id ...]]
           :where ['[?id :payment/amount ?o]
                   '[?id :payment/status :payment-status/pending]]}
          (dc/db conn)
          ids)
   (map last)
   (reduce
    +
    0.0)))

(defn sum-client-pending [clients]
  (->> 
    (dc/q {:find '[?e ?a]
           :in '[$ [?clients ?start ?end]]
           :where '[[(iol-ion.query/scan-payments $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
                    [?e :payment/status :payment-status/pending]
                    [?e :payment/amount ?a]]}
          (dc/db conn)
          [clients
           nil
           nil])
          
   (map last)
   (reduce
    +
    0.0)))

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


    [(->> (hydrate-results ids-to-retrieve db request))
     matching-count
     (sum-visible-pending all-ids)
     (sum-client-pending (extract-client-ids (:clients request)
                                          (:client request)
                                          (:client-id (:query-params request))
                                          (when (:client-code (:query-params request))
                                            [:client/code (:client-code (:query-params request))])))
     ]))

(def query-schema (mc/schema
                   [:maybe [:map {:date-range [:date-range :start-date :end-date]}
                            [:sort {:optional true} [:maybe [:any]]]
                            [:per-page {:optional true :default 25} [:maybe :int]]
                            [:start {:optional true :default 0} [:maybe :int]]
                            [:amount-gte {:optional true} [:maybe :double]]
                            [:amount-lte {:optional true} [:maybe :double]]
                            [:payment-type {:optional true} [:maybe (ref->enum-schema "payment-type")]]
                            [:vendor {:optional true :default nil} [:maybe  [:entity-map {:pull [:db/id :vendor/name]}]]]
                            [:check-number {:optional true} [:maybe [:string {:decode/string strip}]]]
                            [:invoice-number {:optional true} [:maybe [:string {:decode/string strip}]]]
                            [:status {:optional true} [:maybe (ref->enum-schema "payment-status")]]
                            [:exact-match-id {:optional true} [:maybe entity-id]]
                            [:all-selected {:optional true :default nil} [:maybe :boolean]]
                            [:selected {:optional true :default nil} [:maybe [:vector {:coerce? true}
                                                                              entity-id]]]
                            [:start-date {:optional true}
                             [:maybe clj-date-schema]]
                            [:end-date {:optional true}
                             [:maybe clj-date-schema]]]]))

(comment
  (mc/decode query-schema
             {:start " "}
             main-transformer))

;; TODO fix parsing of query params
(def grid-page
  (helper/build {:id                 "entity-table"
                 :nav                com/main-aside-nav
                 :check-boxes? true
                 :page-specific-nav  filters
                 :fetch-page         fetch-page
                 :oob-render
                 (fn [request]
                   [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)
                    (assoc-in (exact-match-id* request)  [1 :hx-swap-oob] true)])
                 :query-schema query-schema
                 :action-buttons (fn [request]
                                   (let [[_ _ visible-in-float total-in-float ] (:page-results request)]
                                     [(com/pill {:color :primary} " Visible in float "
                                                (format "$%,.2f" visible-in-float))
                                      (com/pill {:color :secondary} " Total in float "
                                                (format "$%,.2f" total-in-float))
                                      (when (can? (:identity request) {:subject :payment :activity :bulk-delete})
                                        (com/button {:hx-get    (str (bidi/path-for ssr-routes/only-routes ::route/bulk-delete))
                                                     "x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})"
                                                     "hx-include" "#payment-filters"
                                                     :color     :red}
                                                    "Void selected"))]))
                 :row-buttons (fn [_ entity]
                                [(when (not= :payment-status/voided (:payment/status entity))
                                   (com/icon-button {:hx-delete (bidi/path-for ssr-routes/only-routes
                                                                               ::route/delete
                                                                               :db/id (:db/id entity))
                                                     :hx-confirm "Are you sure you want to void this payment?"}
                                                    svg/trash))])
                 :breadcrumbs        [[:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
                                       "Payments"]]
                 :break-table (fn [request entity]
                                (cond
                                  (= (-> request :query-params :sort first :name) "Vendor")
                                  (-> entity :payment/vendor :vendor/name)

                                  (= (-> request :query-params :sort first :name) "Bank account")
                                  (-> entity :payment/bank-account :bank-account/name)
  
                                  :else nil))
                 :title       (fn [r]
                                (str
                                 (some-> r :rout-params :status name str/capitalize (str " "))
                                 "Payments"))
                 :entity-name "payments"
                 :route       ::route/table
                 :headers     [{:key      "client"
                                :name     "Client"
                                :sort-key "client"
                                :hide?    (fn [args]
                                            (= (count (:clients args)) 1))
                                :render   #(-> % :payment/client :client/code)}
                               {:key      "vendor"
                                :name     "Vendor"
                                :sort-key "vendor"
                                :render   #(-> % :payment/vendor :vendor/name)}
                               {:key      "bank-account"
                                :name     "Bank account"
                                :sort-key "bank-account"
                                :show-starting "xl"
                                :render   (fn [p]
                                            [:div.flex.items-center
                                             (when (:payment/bank-account p)
                                               (bank-account-icon/icon (:payment/bank-account p)))
                                             [:div (-> p :payment/bank-account :bank-account/name)]])}
                               {:key "check-number"
                                :name "Check #"
                                :sort-key "check-number"
                                :render (fn [{:payment/keys [s3-url check-number]}]
                                          (if s3-url
                                            (com/link {:href s3-url :target "_new"} [:div.flex.items-center.gap-x-2  check-number [:div.w-4.h-4 svg/external-link]])
                                            check-number))}
                               {:key "status"
                                :name "Status"
                                :sort-key "status"
                                :render (fn [{:payment/keys [status]}]
                                          (condp  = status
                                            :payment-status/cleared
                                            (com/pill {:color :primary} "cleared")

                                            :payment-status/pending
                                            (com/pill {:color :secondary} "pending")
                                            :payment-status/voided
                                            (com/pill {:color :red} "voided")
                                            nil
                                            ""))}
                               {:key           "date"
                                :name          "Date"
                                :sort-key "date"
                                :show-starting "lg"
                                :render        (fn [{:payment/keys [date]}]
                                                 (some-> date (atime/unparse-local atime/normal-date)))}
                               {:key           "amount"
                               :sort-key "amount" 
                                :name          "Amount"
                                :render        (fn [{:payment/keys [amount]}]
                                                 (some->> amount (format "$%.2f")))}
                               {:key           "links"
                                :name          "Links"
                                :class "w-8"
                                :render        (fn [p]
                                                 (link-dropdown (concat (->> p :invoice-payment/_payment
                                                                             (map :invoice-payment/invoice)
                                                                             (filter identity)
                                                                             (map (fn [invoice]
                                                                                       {:link (hu/url (bidi/path-for ssr-routes/only-routes
                                                                                                                     ::invoice-route/all-page)
                                                                                                      {:exact-match-id (:db/id invoice)})
                                                                                        :content (str "Inv. " (:invoice/invoice-number invoice))})))
                                                                        (some-> p :transaction/_payment ((fn [t]
                                                                                                           [{:link (hu/url (bidi/path-for client-routes/routes
                                                                                                                                          :transactions)
                                                                                                                           {:exact-match-id (:db/id (first t))})
                                                                                                             :color :secondary
                                                                                                             :content "Transaction"}]))))))}]}))

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



(comment
  (mc/decode query-schema {"exact-match-id" "123"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
  (mc/decode query-schema {} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
  (mc/decode query-schema {"exact-match-id" nil} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
  (mc/decode query-schema {"exact-match-id" ""} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
  (mc/decode query-schema {"start-date" "12/21/2023"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
  (mc/decode query-schema {"payment-type" "food"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
  (mc/decode query-schema {"vendor" "87"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))


  (mc/decode query-schema {"start-date" #inst "2023-12-21T08:00:00.000-00:00"} (mt/transformer main-transformer mt/strip-extra-keys-transformer)))

(defn delete [{check :entity :as request identity :identity}]
  (alog/peek ::check-type check)
  (exception->notification
   #(when-not (or (= :payment-status/pending (:payment/status check))
                  (#{:payment-type/cash :payment-type/debit :payment-type/balance-credit} (:payment/type check)))
      (throw (ex-info  "Payment must be pending." {}))))

  (exception->notification
   #(assert-can-see-client identity (:db/id (:payment/client check))))
  (notify-if-locked (:db/id (:payment/client check))
                    (:payment/date check))
  (let [ removing-payments (mapcat (fn [x]
                                    (let [invoice (:invoice-payment/invoice x)
                                          new-balance (+ (:invoice/outstanding-balance invoice)
                                                         (:invoice-payment/amount x))]
                                      [[:db/retractEntity (:db/id x)]
                                       [:upsert-invoice {:db/id                       (:db/id invoice)
                                                         :invoice/outstanding-balance new-balance
                                                         :invoice/status              (if (dollars-0? new-balance)
                                                                                        (:invoice/status invoice)
                                                                                        :invoice-status/unpaid)}]]))
                                  (:invoice-payment/_payment check))
        updated-payment {:db/id (:db/id check)
                         :payment/amount 0.0
                         :payment/status :payment-status/voided}]
    (audit-transact (cond-> removing-payments
                      true (conj updated-payment)
                      (:transaction/_payment check) (conj [:db/retract (:db/id (first (:transaction/_payment check))) 
                                                           :transaction/payment
                                                           (:db/id check)])) 
                    identity)

    (html-response (row* (:identity request) updated-payment {:delete-after-settle? true :class "live-removed"
                                                              :request request})
                   :headers {"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id check))})))

;; TODO use decoding here
(defn bulk-delete-dialog [request]
  (alog/peek :selected (pr-str (:selected (:query-params request))))
  (let [all-selected (:all-selected (:query-params request))
        selected (:selected (:query-params request))
        ids (cond
              all-selected
              (:ids (fetch-ids (dc/db conn) (-> request
                                                (assoc-in [:query-params :start] 0)
                                                (assoc-in [:query-params :per-page]  250))))
              :else
              selected)]
    (modal-response
     (com/modal {}
                (com/modal-card-advanced
                 {}

                 (com/modal-body {}
                                 [:div.flex.flex-col.mt-4.space-y-4.items-center
                                  [:div.w-24.h-24.bg-red-50.rounded-full.p-4.text-red-300

                                   svg/alert]
                                  [:div "You are about to void " (count ids) " payments. Are you sure you want to do this?"]])
                 (com/modal-footer {} [:div.flex.justify-end (com/button {:color :primary
                                                                          :hx-vals (hx/json (mc/encode
                                                                                             query-schema
                                                                                             (dissoc (:query-params request) :sort)
                                                                                             (mt/transformer
                                                                                              main-transformer
                                                                                              dissoc-nil-transformer
                                                                                              mt/strip-extra-keys-transformer)))
                                                                          :hx-delete (hu/url (bidi/path-for ssr-routes/only-routes
                                                                                                            ::route/bulk-delete-confirm))}
                                                                         "Void payments")])))
     :headers (-> {}
                  (assoc "hx-retarget" ".modal-stack")
                  (assoc "hx-reswap" "beforeend")))))

(defn void-payments-internal [all-ids id]
  (let [payments-to-update (->> all-ids
                                (dc/q '[:find (pull ?p [:db/id
                                                        {:invoice-payment/_payment [:invoice-payment/amount
                                                                                    :db/id
                                                                                    {:invoice-payment/invoice [:db/id :invoice/outstanding-balance]}]}])
                                        :in $ [?p ...]
                                        :where
                                        (not [_ :transaction/payment ?p])
                                        (not [?p :payment/status :payment-status/voided])
                                        [?p :payment/client ?c]
                                        [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
                                        [?p :payment/date ?d]
                                        [(>= ?d ?lu)]]
                                      (dc/db conn))
                                (map first))
        transactions-to-unlink (->> payments-to-update
                                    (dc/q '[:find ?p ?t
                                            :in $ [?p ...]
                                            :where [?t :transaction/payment ?p]]
                                          (dc/db conn)))]
    (audit-transact (->> payments-to-update
                         (mapcat (fn [{:keys    [:db/id]
                                       invoices :invoice-payment/_payment}]
                                   (-> [{:db/id          id
                                         :payment/amount 0.0
                                         :payment/status :payment-status/voided}]
                                       ;; TODO this doesn't do anything because payments with invoices are filtered out
                                       (into (->> transactions-to-unlink
                                                  (map (fn [[p t]]
                                                         [:db/retract t :transaction/payment p]))))
                                       (into
                                        (->> invoices
                                             (mapcat (fn [{:keys [:invoice-payment/invoice :db/id :invoice-payment/amount]}]
                                                       (let [new-balance (+ (:invoice/outstanding-balance invoice)
                                                                            amount)]
                                                         [[:db.fn/retractEntity id]
                                                          [:upsert-invoice {:db/id                       (:db/id invoice)
                                                                            :invoice/outstanding-balance new-balance
                                                                            :invoice/status              (if (dollars-0? new-balance)
                                                                                                           (:invoice/status invoice)
                                                                                                           :invoice-status/unpaid)}]])))))))))
                    id)
    (count payments-to-update)))

(defn bulk-delete-dialog-confirm [request]
  (alog/peek (:form-params request))
  (let [all-selected (:all-selected (:form-params request))
        selected (:selected (:form-params request))
        ids (cond
              all-selected
              (:ids (fetch-ids (dc/db conn) (-> request
                                                (assoc :query-params (:form-params request))
                                                (assoc-in [:query-params :start] 0)
                                                (assoc-in [:query-params :per-page]  250))))


              :else
              selected)
        updated-count (void-payments-internal ids (:identity request))]

    (html-response [:div]
                   :headers {"hx-trigger" (hx/json {:modalclose   ""
                                                    :notification (format "Successfully voided %d of %d payments."
                                                                          updated-count
                                                                          (count ids))})})))

(defn wrap-status-from-source [handler]
  (fn [{:keys [matched-current-page-route] :as request}]
    (let [ request (cond-> request
                    (= ::route/cleared-page matched-current-page-route) (assoc-in [:route-params :status] :payment-status/cleared)
                    (= ::route/pending-page matched-current-page-route) (assoc-in [:route-params :status] :payment-status/pending)
                    (= ::route/voided-page matched-current-page-route) (assoc-in [:route-params :status] :payment-status/voided)
                    (= ::route/all-page matched-current-page-route) (assoc-in [:route-params :status] nil))]
      (handler request))))

(def key->handler
  (apply-middleware-to-all-handlers
   {::route/cleared-page              (-> (helper/page-route grid-page)
                                          (wrap-implied-route-param :status :payment-status/cleared))
    ::route/pending-page              (-> (helper/page-route grid-page)
                                          (wrap-implied-route-param :status :payment-status/pending))
    ::route/voided-page              (-> (helper/page-route grid-page)
                                          (wrap-implied-route-param :status :payment-status/voided))
    ::route/all-page              (-> (helper/page-route grid-page)
                                      (wrap-implied-route-param :status nil))

    ::route/delete (-> delete
                       (wrap-entity [:route-params :db/id] default-read)
                       (wrap-schema-enforce :route-params [:map [:db/id entity-id]]))
    ::route/bulk-delete-confirm  (-> bulk-delete-dialog-confirm
                                     (wrap-schema-enforce :form-schema  query-schema)
                                     (wrap-admin))
    ::route/bulk-delete  (-> bulk-delete-dialog
                             (wrap-admin))


    ::route/table             (helper/table-route grid-page)}
   (fn [h]
     (-> h
         (wrap-copy-qp-pqp)
         (wrap-apply-sort grid-page)
         (wrap-merge-prior-hx)
         (wrap-status-from-source)
         (wrap-schema-enforce :query-schema query-schema)
         (wrap-schema-enforce :hx-schema query-schema)
         (wrap-client-redirect-unauthenticated)))))