(ns auto-ap.ssr.admin.clients
  (:require
   [auto-ap.datomic
             :refer [add-sorter-fields apply-pagination apply-sort-3
                     audit-transact conn merge-query pull-attr pull-id
                     pull-many query2]]
   [auto-ap.graphql.utils :refer [extract-client-ids]]
   [auto-ap.logging :as alog]
   [auto-ap.query-params :refer [wrap-copy-qp-pqp]]
   [auto-ap.routes.admin.clients :as route]
   [auto-ap.routes.indicators :as indicators]
   [auto-ap.routes.queries :as q]
   [auto-ap.routes.utils
             :refer [wrap-admin wrap-client-redirect-unauthenticated]]
   [auto-ap.solr :as solr]
   [auto-ap.square.core3 :as square]
   [auto-ap.ssr-routes :as ssr-routes]
   [auto-ap.ssr.common-handlers :refer [add-new-entity-handler
                                        add-new-primitive-handler]]
   [auto-ap.ssr.components :as com]
   [auto-ap.ssr.components.multi-modal :as mm]
   [auto-ap.ssr.form-cursor :as fc]
   [auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
   [auto-ap.ssr.hiccup-helper :as hh]
   [auto-ap.ssr.hx :as hx]
   [auto-ap.ssr.indicators :as i]
   [auto-ap.ssr.svg :as svg]
   [auto-ap.ssr.utils
             :refer [apply-middleware-to-all-handlers entity-id
                     form-validation-error html-response many-entity
                     many-entity-custom modal-response ref->enum-schema strip
                     temp-id wrap-entity wrap-merge-prior-hx
                     wrap-schema-enforce]]
   [auto-ap.time :as atime]
   [bidi.bidi :as bidi]
   [cheshire.core :as cheshire]
   [clj-time.coerce :as coerce]
   [clj-time.core :as time]
   [clojure.java.io :as io]
   [clojure.string :as str]
   [datomic.api :as dc]
   [hiccup.util :as hu]
   [malli.core :as mc]
   [malli.transform :as mt]
   [malli.util :as mut]
   [manifold.deferred :as de])
  (:import
   [java.util UUID]))


;; TODO make more reusable malli schemas, use unions if it would be helpful
;; TODO copy save logic from graphql version
;; TODO cash drawer shift
;; TODO a few bug fixes from slack
;; TOOD check pinecone

(def query-schema (mc/schema
                   [:maybe [:map
                            [:sort {:optional true} [:maybe [:any]]]
                            [:per-page {:optional true :default 25} [:maybe :int]]
                            [:start {:optional true :default 0} [:maybe :int]]
                            [:code {:optional true} [:maybe {:decode/string strip} :string]]
                            [:name {:optional true} [:maybe {:decode/string strip} :string]]
                            [:group {:optional true} [:maybe {:decode/string strip} :string]]
                            [:select {:optional true :default "all"}
                             [:maybe
                              [:enum
                               "" "all" "only-mine"]]]]]))



(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
                                        ::route/table)
          "hx-target"    "#entity-table"
          "hx-indicator" "#entity-table"}

   [:fieldset.space-y-6
    (com/field {:label "Name"}
               (com/text-input {:name        "name"
                                :id          "name"
                                :class       "hot-filter"
                                :value       (:name (:query-params request))
                                :placeholder "Best Restaurant LLC"
                                :size        :small}))

    (com/field {:label "Code"}
               (com/text-input {:name        "code"
                                :id          "code"
                                :class       "hot-filter"
                                :value       (:code (:query-params request))
                                :placeholder "BRLC"
                                :size        :small}))
    (com/field {:label "Group"}
               (com/text-input {:name        "group"
                                :id          "group"
                                :class       "hot-filter"
                                :value       (:group (:query-params request))
                                :placeholder "NTG"
                                :size        :small}))
    (com/field {:label "Select"}
               (com/radio-card {:size    :small
                                :name    "select"
                                :value (:select (:query-params request))
                                :options [{:value   ""
                                           :content "All"}
                                          {:value   "only-mine"
                                           :content "Only mine"}]}))]])

(def default-read '[:db/id
                    :client/name
                    :client/code
                    :client/locations
                    :client/groups
                    :client/matches
                    :client/week-a-credits
                    :client/week-b-credits
                    :client/week-a-debits
                    :client/week-b-debits
                    :client/square-auth-token

                    :client/feature-flags

                    {:client/bank-accounts [:bank-account/code
                                            :db/id
                                            :bank-account/bank-name
                                            :bank-account/numeric-code
                                            :bank-account/name
                                            :bank-account/include-in-reports
                                            :bank-account/visible
                                            :bank-account/number
                                            :bank-account/bank-code
                                            :bank-account/sort-order
                                            :bank-account/routing
                                            :bank-account/check-number
                                            :bank-account/use-date-instead-of-post-date?
                                            {:bank-account/yodlee-account [:db/id :yodlee-account/name]}
                                            {:bank-account/plaid-account [:db/id :plaid-account/name]}
                                            {:bank-account/intuit-bank-account [:db/id :intuit-bank-account/name]}

                                            [:bank-account/start-date :xform clj-time.coerce/from-date]
                                            {[:bank-account/type :xform iol-ion.query/ident]  [:db/ident]}
                                            {:bank-account/integration-status
                                             [[:integration-status/last-updated :xform clj-time.coerce/from-date]
                                              {[:integration-status/state :xform iol-ion.query/ident] [:db/ident]}]}]
                     :client/address [:address/street1 :address/street2 :address/city :address/state :address/zip :db/id]
                     :client/square-locations [:db/id
                                               :square-location/name
                                               :square-location/client-location
                                               :square-location/square-id]}
                    [:client/locked-until :xform clj-time.coerce/from-date]
                    {:client/emails [:email-contact/description :email-contact/email :db/id]
                     :client/location-matches [:location-match/matches :location-match/location :db/id]}])

(defn fetch-ids [db request]
  (let [query-params (:query-params request)
        valid-clients (extract-client-ids #_(:clients request)
                       (map first (dc/q '[:find ?c :where [?c :client/code]] (dc/db conn)))
                                          (:client-id query-params)
                                          (when (:client-code query-params)
                                            [:client/code (:client-code query-params)]))
        query (cond-> {:query {:find []
                               :in ['$ '[?e ...]]
                               :where []}
                       :args [db valid-clients]}
                (:sort query-params) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]

                                                         "code" ['[?e :client/code ?sort-code]]}
                                                        query-params)
                (= "only-mine" (:select query-params))
                (merge-query {:query {:in    ['?uid]
                                      :where ['[?uid :user/clients ?e]]}
                              :args  [(:db/id (:identity request))]})

                (:group query-params)
                (merge-query {:query {:in    ['?g]
                                      :where ['[?e :client/groups ?g]]}
                              :args  [(clojure.string/upper-case (:group query-params))]})


                (not (str/blank? (some-> query-params :code)))
                (merge-query {:query {:in    ['?code]
                                      :where ['[?e :client/code ?code]]}
                              :args  [(clojure.string/upper-case (:code query-params))]})

                (not (str/blank? (:name query-params)))
                (merge-query {:query {:in ['?description]
                                      :where ['[?e :client/name ?d]
                                              '[(clojure.string/lower-case ?d) ?d2]
                                              '[(clojure.string/includes? ?d2 ?description)]]}
                              :args [(clojure.string/lower-case (:name query-params))]})

                true
                (merge-query {:query {:find ['?e]
                                      :where ['[?e :client/name]]}}))]

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

(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                 "entity-table"
                 :nav                com/admin-aside-nav
                 :page-specific-nav  filters
                 :fetch-page         fetch-page
                 :action-buttons (fn [_]
                                   [(com/button {:hx-get    (str (bidi/path-for ssr-routes/only-routes ::route/new-dialog))
                                                 :color     :primary}
                                                "New Client")])
                 :row-buttons (fn [_ entity]
                                [(com/icon-button {:hx-get  (bidi/path-for ssr-routes/only-routes
                                                                           ::route/biweekly-sales-powerquery
                                                                           :db/id (:db/id entity))}
                                                  svg/dollar-tag)
                                 (com/icon-button {:hx-get (bidi/path-for ssr-routes/only-routes
                                                                          ::route/edit-dialog
                                                                          :db/id (:db/id entity))}
                                                  svg/pencil)])
                 :breadcrumbs        [[:a {:href (bidi/path-for ssr-routes/only-routes :admin)}
                                       "Admin"]
                                      [:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
                                       "Clients"]]
                 :title       "Clients"
                 :entity-name "Client"
                 :query-schema query-schema
                 :route       ::route/table
                 :headers     [{:key      "name"
                                :name     "Name"
                                :sort-key "name"
                                :render   :client/name}
                               {:key      "code"
                                :name     "Code"
                                :sort-key "code"
                                :render   :client/code}
                               {:key "locations"
                                :name "Locations"
                                :render (fn [{:client/keys [locations]}]
                                          [:div.flex.gap-2
                                           (for [l locations]
                                             (com/pill {:color :primary} l))])}
                               {:key "emails"
                                :name "Emails"
                                :show-starting "lg"
                                :render (fn [{:client/keys [emails]}]
                                          [:div.flex.gap-2.flex-wrap
                                           (for [{:email-contact/keys [email description]} emails]
                                             (com/pill {:color :secondary} (format "%s - %s" description email)))])}
                               {:key           "status"
                                :name          "Status"
                                :show-starting "lg"
                                :render        (fn [{:client/keys [locked-until bank-accounts]}]
                                                 [:div.flex.gap-2.flex-wrap
                                                  (if locked-until
                                                    (let [days-since-locked (try (time/in-days (time/interval locked-until (time/now)))
                                                                                 (catch Exception _
                                                                                   0))]
                                                      (cond
                                                        (< days-since-locked 90)
                                                        (com/pill {:color :primary} (format "Locked %s" (atime/unparse-local locked-until atime/normal-date)))

                                                        (< days-since-locked 365)
                                                        (com/pill {:color :yellow} (format "Locked %s" (atime/unparse-local locked-until atime/normal-date)))

                                                        :else
                                                        (com/pill {:color :red} (format "Locked %s" (atime/unparse-local locked-until atime/normal-date)))))
                                                    (com/pill {:color :red} (format "Not locked")))
                                                  (for [ba bank-accounts]
                                                    (com/pill {:color (cond (#{:integration-state/failed :integration-state/unauthorized} (-> ba :bank-account/integration-status :integration-status/state))
                                                                            :red

                                                                            :else
                                                                            :secondary)}
                                                              (:bank-account/code ba)))]
                                                 #_[:div.flex.gap-2.flex-wrap
                                                    (for [{:email-contact/keys [email description]} emails]
                                                      (com/pill {:color :secondary} (format "%s - %s" description email)))])}]}))

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



(def bank-account-schema [:and [:map
                                [:db/id [:or entity-id temp-id]]
                                [:bank-account/name :string]
                                [:bank-account/code :string]

                                [:bank-account/type [:maybe (ref->enum-schema "bank-account-type")]]
                                [:bank-account/numeric-code {:optional true} [:maybe :int]]
                                [:bank-account/check-number {:optional true} [:maybe :int]]
                                [:bank-account/bank-name {:optional true} [:maybe :string]]
                                [:bank-account/number {:optional true} [:maybe :string]]
                                [:bank-account/routing {:optional true} [:maybe :string]]
                                [:bank-account/bank-code {:optional true} [:maybe :string]]
                                [:bank-account/sort-order {:default 0} [:maybe :int]]
                                [:bank-account/yodlee-account {:optional true} [:maybe entity-id]]
                                [:bank-account/plaid-account {:optional true} [:maybe entity-id]]
                                [:bank-account/intuit-bank-account {:optional true} [:maybe entity-id]]
                                [:bank-account/include-in-reports {:default false}
                                 [:boolean {:decode/string {:enter #(if (= % "on") true

                                                                        (boolean %))}}]]
                                [:bank-account/visible {:default false}
                                 [:boolean {:decode/string {:enter #(if (= % "on") true

                                                                        (boolean %))}}]]
                                [:bank-account/use-date-instead-of-post-date? {:default false}
                                 [:boolean {:decode/string {:enter #(if (= % "on") true

                                                                        (boolean %))}}]]
                                [:bank-account/start-date {:optional true} [:maybe {:decode/arbitrary (fn [m]
                                                                                                        (if (string? m)
                                                                                                          (clj-time.coerce/to-date (auto-ap.time/parse m atime/normal-date))
                                                                                                          m))}
                                                                            inst?]]]
                          [:fn {:error/message "Bank account financial code is required if the 'Include in Reports?' flag is true."
                                :error/path [:bank-account/numeric-code]}
                           (fn [{:bank-account/keys [include-in-reports numeric-code] :as g}]
                             (cond
                               (and include-in-reports (not numeric-code))
                               false
                               :else
                               true))]])

(def form-schema-2 (mc/schema
                    [:map
                     [:db/id {:optional true} [:maybe entity-id]]
                     [:client/name :string]
                     [:client/code :string]
                     [:client/feature-flags {:optional true}
                      [:maybe [:vector {:decode/arbitrary (fn [m] (if (map? m)
                                                                    (vals m)
                                                                    m))}
                               :string]]]

                     [:client/locations [:vector {:decode/arbitrary (fn [m] (if (map? m)
                                                                              (vals m)
                                                                              m))} :string]]
                     [:client/groups {:optional true :default []} [:maybe [:vector {:decode/arbitrary (fn [m] (if (map? m)
                                                                                                                (vals m)
                                                                                                                m))} :string]]]
                     [:client/emails {:optional true} [:maybe (many-entity {}
                                                                           [:db/id [:or entity-id temp-id]]
                                                                           [:email-contact/description :string]
                                                                           [:email-contact/email :string])]]
                     [:client/locked-until {:optional true} [:maybe {:decode/arbitrary (fn [m]
                                                                                         (if (string? m)
                                                                                           (auto-ap.time/parse m atime/normal-date)
                                                                                           m))}
                                                             inst?]]
                     [:client/location-matches {:optional true}
                      [:maybe (many-entity {}
                                           [:db/id [:or entity-id temp-id]]
                                           [:location-match/matches [:vector {:decode/arbitrary (fn [m] (if (map? m)
                                                                                                          (vals m)
                                                                                                          m))}
                                                                     :string]]
                                           [:location-match/location :string])]]
                     [:client/square-auth-token {:optional true} [:maybe :string]]
                     [:client/square-locations {:optional true}
                      [:maybe (many-entity {}
                                           [:db/id [:or entity-id temp-id]]
                                           [:square-location/name :string]
                                           [:square-location/square-id :string]
                                           [:square-location/client-location :string])]]

                     [:client/bank-accounts {:default []}
                      [:maybe (many-entity-custom {}
                                                  [:and
                                                   [:map
                                                    
                                                    [:db/id [:or entity-id temp-id]]
                                                    [:bank-account/name :string]
                                                    
                                                    [:bank-account/code :string]
                                                    [:bank-account/type [:maybe (ref->enum-schema "bank-account-type")]]
                                                    [:bank-account/numeric-code {:optional true} [:maybe :int]]
                                                    [:bank-account/sort-order {:default 0} [:maybe :int]]
                                                    [:bank-account/routing {:optional true} [:maybe :string]]
                                                    [:bank-account/bank-code {:optional true} [:maybe :string]]
                                                    [:bank-account/bank-name {:optional true} [:maybe :string]]
                                                    [:bank-account/number {:optional true} [:maybe :string]]
                                                    [:bank-account/check-number {:optional true} [:maybe :int]]
                                                    [:bank-account/yodlee-account {:optional true} [:maybe entity-id]]
                                                    [:bank-account/plaid-account {:optional true} [:maybe entity-id]]
                                                    [:bank-account/intuit-bank-account {:optional true} [:maybe entity-id]]
                                                    [:bank-account/include-in-reports {:default false}
                                                     [:boolean {:decode/string {:enter #(if (= % "on") true
                                                                                            
                                                                                            (boolean %))}}]]
                                                    [:bank-account/visible {:default false}
                                                     [:boolean {:decode/string {:enter #(if (= % "on") true
                                                                                            
                                                                                            (boolean %))}}]]
                                                    [:bank-account/use-date-instead-of-post-date? {:default false}
                                                     [:boolean {:decode/string {:enter #(if (= % "on") true
                                                                                            
                                                                                            (boolean %))}}]]
                                                    [:bank-account/start-date {:optional true} [:maybe {:decode/arbitrary (fn [m]
                                                                                                                            (if (string? m)
                                                                                                                              (clj-time.coerce/to-date (auto-ap.time/parse m atime/normal-date))
                                                                                                                              m))}
                                                                                                inst?]]]
                                                   [:fn {:error/message "Bank account financial code is required if the 'Include in Reports?' flag is true."
                                                         :error/path [:bank-account/numeric-code]}
                                                    (fn [{:bank-account/keys [include-in-reports numeric-code] :as g}]
                                                      (cond
                                                        (and include-in-reports (not numeric-code))
                                                        false
                                                        :else
                                                        true))]])]]
                     [:client/matches {:optional true :default []} [:vector {:decode/arbitrary (fn [m] (if (map? m)
                                                                                                         (vals m)
                                                                                                         m))}
                                                                    :string]]
                     [:client/address {:optional true}
                      [:maybe
                       [:map
                        [:db/id {:optional true} [:maybe [:or entity-id temp-id]]]
                        [:address/street1 {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                        ;; I don't like using a default because otherwise it wouldn't allow creating an empty address
                        [:address/street2 {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                        [:address/city {:optional true}  [:maybe [:string {:min 2 :decode/string strip}]]]
                        [:address/state {:optional true}  [:maybe [:string {:min 2 :max 2 :decode/string strip}]]]
                        [:address/zip {:optional true}  [:maybe [:string {:min 5 :max 5 :decode/string strip}]]]]]]
                     [:client/week-a-credits {:optional true} [:maybe :double]]
                     [:client/week-a-debits {:optional true} [:maybe :double]]
                     [:client/week-b-credits {:optional true} [:maybe :double]]
                     [:client/week-b-debits {:optional true} [:maybe :double]]]))





(defn email-contact-row [email-contact-cursor]
  (com/data-grid-row
   (-> {:x-data   (hx/json {:show      (boolean (not (fc/field-value (:new? email-contact-cursor))))})
        :data-key "show"
        :x-ref    "p"}
       hx/alpine-mount-then-appear)
   (list
    (fc/with-field :db/id
      (com/hidden {:name  (fc/field-name)
                   :value (fc/field-value)}))
    (com/data-grid-cell {}
                        (fc/with-field :email-contact/description
                          (com/validated-field {:errors (fc/field-errors)
                                                :class  "shrink-0"}
                                               (com/text-input {:name        (fc/field-name)
                                                                :error?      (fc/error?)
                                                                :placeholder "Suite 300"
                                                                :class       "w-full"
                                                                :value       (fc/field-value)}))))
    (com/data-grid-cell {}
                        (fc/with-field :email-contact/email
                          (com/validated-field {:errors (fc/field-errors)
                                                :class  "shrink-0"}
                                               (com/text-input {:name        (fc/field-name)
                                                                :error?      (fc/error?)
                                                                :placeholder "Suite 300"
                                                                :class       "w-full"
                                                                :value       (fc/field-value)}))))
    (com/data-grid-cell {:class "align-top"}
                        (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))

(defn location-row [_]
  (com/data-grid-row
   {:x-ref "p"
    :x-data (hx/json {})}
   (com/data-grid-cell
    {}
    (com/validated-field {}
                         (com/text-input {:name  (fc/field-name)
                                          :value (fc/field-value)
                                          :class "w-24"})))
   (com/data-grid-cell {:class "align-top"}
                       (com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))

(defn group-row [_]
  (com/data-grid-row
   {:x-ref "p"
    :x-data (hx/json {})}
   (com/data-grid-cell
    {}
    (com/validated-field {}
                         (com/text-input {:name  (fc/field-name)
                                          :value (fc/field-value)
                                          :class "w-24"})))
   (com/data-grid-cell {:class "align-top"}
                       (com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))

(defn feature-flag-row [_]
  (com/data-grid-row
   {:x-ref "p"
    :x-data (hx/json {})}
   (com/data-grid-cell
    {}
    (com/validated-field {}
                         (com/select {:name        (fc/field-name)
                                      :allow-blank? true
                                      :error?      (fc/error?)
                                      :class "w-full"
                                      :value       (fc/field-value)
                                      :options [["new-square" "New Square+Ezcater (no effect)"]
                                                ["manually-pay-cintas" "Manually Pay Cintas"]
                                                ["include-in-ntg-corp-reports" "Include in NTG Corporate reports"]
                                                ["import-custom-amount" "Import Custom Amount Line Items from Square"]
                                                ["code-sysco-items" "Code individual sysco line items"]
                                                ["report-pedantic" "Show two decimals in reports"]]})))

   (com/data-grid-cell {:class "align-top"}
                       (com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))


(defn- dialog-header [step]
  [:div.flex [:div.p-2 (mm/step-name step)] [:p.ml-2.rounded.bg-gray-50.p-2.dark:bg-gray-600
                                             [:span {:x-text "clientName"}]]])


(defrecord InfoModal [linear-wizard]
  mm/ModalWizardStep
  (step-name [_]
    "Info")
  (step-key [_]
    :info)

  (edit-path [_ _]
    [])

  (step-schema [_]
    (mut/select-keys (mm/form-schema linear-wizard) #{:client/name :client/code :client/locations}))

  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head (dialog-header this)
     :body (mm/default-step-body
            {}
            [:div.flex.space-x-2
             (fc/with-field :client/name
               (com/validated-field {:label  "Name"
                                     :errors (fc/field-errors)}
                                    (com/text-input {:name      (fc/field-name)
                                                     :value     (fc/field-value)
                                                     :x-model   "clientName"
                                                     :autofocus true
                                                     :class     "w-96"})))

             (fc/with-field :client/code
               (com/validated-field {:label  "Code"
                                     :errors (fc/field-errors)}
                                    (list
                                     (com/text-input {:name     (fc/field-name)
                                                      :disabled (if (:db/id (:entity linear-wizard)) true false)
                                                      :value    (fc/field-value)
                                                      :class    "w-24"})
                                     (when (:db/id (:entity linear-wizard))
                                       (com/hidden {:name (fc/field-name)
                                                    :value (fc/field-value)})))))]
            (fc/with-field :client/locked-until
              (com/validated-field {:label  "Locked Until"
                                    :errors (fc/field-errors)}
                                   (com/date-input {:name      (fc/field-name)
                                                    :placeholder "Disallow changes before this date"
                                                    :value
                                                    (some-> (fc/field-value)
                                                            (atime/unparse-local atime/normal-date))

                                                    :class     "w-24"})))

            (fc/with-field :client/locations
              (com/validated-field
               {:errors (fc/field-errors)
                :label  "Locations"}
               (com/data-grid {:headers [(com/data-grid-header {} "Location")
                                         (com/data-grid-header {:class "w-16"})]}
                              (fc/cursor-map #(location-row %))
                              (com/data-grid-new-row {:colspan 2
                                                      :hx-get  (bidi/path-for ssr-routes/only-routes ::route/new-location)
                                                      :index   (count (fc/field-value))}
                                                     "New location")))))
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate)))


(defn match-row [_]
  (com/data-grid-row
   {:x-ref "p"
    :x-data (hx/json {})}
   (com/data-grid-cell
    {}
    (com/validated-field {}
                         (com/text-input {:name  (fc/field-name)
                                          :value (fc/field-value)
                                          :class "w-full"})))
   (com/data-grid-cell {:class "align-top"}
                       (com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))

(defn location-match-row [location-match-cursor]
  (com/data-grid-row
   (-> {:x-data   (hx/json {:show      (boolean (not (fc/field-value (:new? location-match-cursor))))})
        :data-key "show"
        :x-ref    "p"}
       hx/alpine-mount-then-appear)

   (fc/with-field :db/id
     (com/hidden {:name  (fc/field-name)
                  :value (fc/field-value)}))
   (com/data-grid-cell {}
                       (fc/with-field-default :location-match/matches [""]
                         (fc/with-field 0
                           (com/validated-field {:errors (fc/field-errors)
                                                 :class  "shrink-0"}
                                                (com/text-input {:name        (fc/field-name)
                                                                 :error?      (fc/error?)
                                                                 :placeholder "Suite 300"
                                                                 :class       "w-full"
                                                                 :value       (fc/field-value)})))))
   (com/data-grid-cell {}
                       (fc/with-field :location-match/location
                         (com/validated-field {:errors (fc/field-errors)
                                               :class  "shrink-0"}
                                              (com/text-input {:name        (fc/field-name)
                                                               :error?      (fc/error?)
                                                               :placeholder "Suite 300"
                                                               :class       "w-24"
                                                               :value       (fc/field-value)}))))
   (com/data-grid-cell {:class "align-top"}
                       (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))



(defrecord MatchesModal [linear-wizard]
  mm/ModalWizardStep
  (step-name [_]
    "Matches")
  (step-key [_]
    :matches)
  (edit-path [_ _]
    [])

  (step-schema [_]
    (mut/select-keys (mm/form-schema linear-wizard) #{:client/matches :client/location-matches}))

  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head (dialog-header this)
     :body (mm/default-step-body {}
                                 (fc/with-field :client/matches
                                   (com/validated-field
                                    {:errors (fc/field-errors)
                                     :label  "Matches"}
                                    (com/data-grid {:headers [(com/data-grid-header {} "Match")
                                                              (com/data-grid-header {:class "w-16"})]}
                                                   (fc/cursor-map #(match-row %))
                                                   (com/data-grid-new-row {:colspan 2
                                                                           :hx-get  (bidi/path-for ssr-routes/only-routes ::route/new-match)
                                                                           :index   (count (fc/field-value))}
                                                                          "New Match"))))
                                 (fc/with-field :client/location-matches
                                   (com/validated-field
                                    {:errors (fc/field-errors)
                                     :label  "Location Matches"}
                                    (com/data-grid {:headers [(com/data-grid-header {} "Match")
                                                              (com/data-grid-header {} "location")
                                                              (com/data-grid-header {:class "w-16"})]}
                                                   (fc/cursor-map #(location-match-row %))
                                                   (com/data-grid-new-row {:colspan 3
                                                                           :hx-get  (bidi/path-for ssr-routes/only-routes ::route/new-location-match)
                                                                           :index   (count (fc/field-value))}
                                                                          "New Match"))))
                                 [:div])
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate)))

(defrecord ContactModal [linear-wizard]
  mm/ModalWizardStep
  (step-name [_]
    "Contact")

  (step-key [_]
    :contact)


  (edit-path [_ _]
    [])

  (step-schema [_]
    (mut/select-keys (mm/form-schema linear-wizard) #{:client/address}))

  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head (dialog-header this)
     :body (mm/default-step-body {}

                                 (fc/with-field-default :client/address {}
                                   [:div.flex.flex-col.w-full
                                    (when (:db/id @fc/*current*)
                                      (fc/with-field :db/id
                                        (com/hidden {:name  (fc/field-name)
                                                     :value (fc/field-value)})))
                                    (fc/with-field :address/street1

                                      (com/validated-field {:label  "Street"
                                                            :errors (fc/field-errors)}
                                                           (com/text-input {:name        (fc/field-name)
                                                                            :error?      (fc/error?)
                                                                            :autofocus   true
                                                                            :class       "w-full"
                                                                            :placeholder "1200 Pennsylvania Avenue"
                                                                            :value       (fc/field-value)})))
                                    (fc/with-field :address/street2
                                      (com/validated-field {:errors (fc/field-errors)}
                                                           (com/text-input {:name        (fc/field-name)
                                                                            :error?      (fc/error?)
                                                                            :class       "w-full"
                                                                            :placeholder "Suite 300"
                                                                            :value       (fc/field-value)})))
                                    [:div.flex.w-full.space-x-4
                                     (fc/with-field :address/city
                                       (com/validated-field {:errors (fc/field-errors)
                                                             :class  "w-full grow shrink"}
                                                            (com/text-input {:name        (fc/field-name)
                                                                             :error?      (fc/error?)
                                                                             :class       "w-full"
                                                                             :placeholder "Suite 300"
                                                                             :value       (fc/field-value)})))
                                     (fc/with-field :address/state
                                       (com/validated-field {:errors (fc/field-errors)
                                                             :class  "w-16 shrink-0"}
                                                            (com/text-input {:name        (fc/field-name)
                                                                             :error?      (fc/error?)
                                                                             :class       "w-full"
                                                                             :placeholder "Suite 300"
                                                                             :value       (fc/field-value)})))
                                     (fc/with-field :address/zip
                                       (com/validated-field {:errors (fc/field-errors)
                                                             :class  "w-24 shrink-0"}
                                                            (com/text-input {:name        (fc/field-name)
                                                                             :error?      (fc/error?)
                                                                             :placeholder "Suite 300"
                                                                             :class       "w-full"
                                                                             :value       (fc/field-value)})))]])

                                 (fc/with-field :client/emails
                                   (com/validated-field
                                    {:errors (fc/field-errors)
                                     :label  "Email Contacts"}
                                    (com/data-grid {:headers [(com/data-grid-header {} "Name")
                                                              (com/data-grid-header {} "Email")
                                                              (com/data-grid-header {:class "w-16"})]}
                                                   (fc/cursor-map #(email-contact-row %))
                                                   (com/data-grid-new-row {:colspan 3
                                                                           :hx-get  (bidi/path-for ssr-routes/only-routes ::route/new-email-contact)
                                                                           :index   (count (fc/field-value))}
                                                                          "New email contact")))))
     :footer (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate)))

(defn bank-account-card-base [{:keys [bg-color text-color icon bank-account]}]
  [:div {:class "w-[30em] cursor-move"}
   (com/card {:class "w-full"}
             [:div.flex.items-stretch
              (com/hidden {:name "item"
                           :value (fc/field-value (:db/id bank-account))})
              [:div.grow-0.flex.flex-col.justify-center
               [:div.p-1.m-2.rounded-full
                {:class
                 bg-color}
                [:div {:class
                       (hh/add-class "p-1.5 w-8 h-8" text-color)}
                 icon]]]
              [:div.flex.flex-col.grow.m-2
               [:div.font-medium.text-gray-700  (fc/field-value (:bank-account/name bank-account))]
               [:div.font-light.text-gray-600 (fc/field-value (:bank-account/bank-name bank-account))]]
              [:div.grow-0.p-4
               (com/a-icon-button  {:hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/navigate)
                                                    {:from (mm/encode-step-key :bank-accounts)
                                                     :to (mm/encode-step-key [:bank-account (fc/field-value (:db/id bank-account))])})}
                                   svg/pencil)]])])


(defmulti bank-account-card  (comp deref :bank-account/type))
(defmethod bank-account-card :bank-account-type/cash [bank-account]
  (bank-account-card-base {:bg-color "bg-green-50"
                           :text-color "text-green-600"
                           :icon svg/dollar
                           :bank-account bank-account}))

(defmethod bank-account-card
  :bank-account-type/credit
  [bank-account]
  (bank-account-card-base {:bg-color "bg-purple-50"
                           :text-color "text-purple-600"
                           :icon svg/credit-card
                           :bank-account bank-account}))

(defmethod bank-account-card
  :bank-account-type/check [bank-account]
  (bank-account-card-base {:bg-color "bg-blue-50"
                           :text-color "text-blue-600"
                           :icon svg/check
                           :bank-account bank-account}))


(defmulti bank-account-form  (comp deref :bank-account/type))
(defmethod bank-account-form :bank-account-type/cash [bank-account]
  [:div
   [:h2.text-lg
    (if (:new @bank-account)
      "New Cash Account"
      (str "Edit Cash Account: " (:bank-account/name @bank-account)))]
   (fc/with-field :db/id
     (com/hidden {:name (fc/field-name)
                  :value (fc/field-value)}))
   (fc/with-field :bank-account/type
     (com/hidden {:name (fc/field-name)
                  :value (name (fc/field-value))}))
   [:div.flex.space-x-2
    (fc/with-field :bank-account/name
      (com/validated-field {:errors (fc/field-errors)
                            :label "Nickname"
                            :class "w-[20em]"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :autofocus true
                                            :placeholder "BofA Checking"
                                            :class       "w-full"
                                            :value       (fc/field-value)})))
    (fc/with-field :bank-account/code
      (com/validated-field {:errors (fc/field-errors)
                            :label "Code"
                            :class "w-20"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :disabled (not (:new? @bank-account))
                                            :placeholder "NGOM-CASH"
                                            :class       "w-full"
                                            :value       (fc/field-value)})
                           (when-not (:new? @bank-account)
                             (com/hidden {:name (fc/field-name)
                                          :value (fc/field-value)}))))]

   (fc/with-field :bank-account/numeric-code
     (com/validated-field {:errors (fc/field-errors)
                           :label "Financial code"}
                          [:div {:class "w-[5em]"}
                           (com/int-input {:name        (fc/field-name)
                                           :error?      (fc/error?)
                                           :placeholder "11101"
                                           :class       "w-[5em]"
                                           :value       (fc/field-value)})]))

   (fc/with-field :bank-account/start-date
     [:div.flex.space-x-2.items-center
      (com/validated-field {:errors (fc/field-errors)
                            :label "Start Date"}
                           [:div {:class "w-[7em]"}
                            (com/date-input {:name        (fc/field-name)
                                             :error?      (fc/error?)
                                             :placeholder "12/01/2023"
                                             :hx-get (bidi/path-for ssr-routes/only-routes
                                                                    ::indicators/days-ago)
                                             :hx-trigger "change"
                                             :hx-target "#days-indicator"
                                             :hx-vals "js:{date: event.target.value}"
                                             :hx-swap "innerHTML"
                                             :class       "w-[5em]"
                                             :value       (some-> (fc/field-value)
                                                                  (clj-time.coerce/to-date-time)
                                                                 ;; todo do date coercion in the input
                                                                  (atime/unparse-local atime/normal-date))})])
      [:div#days-indicator
       (i/days-ago* (some-> (fc/field-value)))]])

   (fc/with-field :bank-account/include-in-reports
     (com/checkbox {:name (fc/field-name)
                    :value (boolean (fc/field-value))
                    :checked (fc/field-value)}
                   "Include in reports"))
   [:div
    (fc/with-field :bank-account/visible
      (com/checkbox {:name (fc/field-name)
                     :value (boolean (fc/field-value))
                     :checked (fc/field-value)}
                    "Visible for payment"))]])



(defn- plaid-account-select [client-id]
  (fc/with-field :bank-account/plaid-account
    (com/validated-field {:errors (fc/field-errors)
                          :label "Plaid account"
                          :class "w-[20em]"
                          :x-data (hx/json {:plaidAccount (fc/field-value)})}
                         [:div.flex.gap-2.items-center
                          (com/select {:name        (fc/field-name)
                                       :allow-blank? true
                                       :error?      (fc/error?)
                                       :class "w-full"
                                       :value       (fc/field-value)
                                       :x-model "plaidAccount"
                                       :options
                                       (when client-id
                                         (dc/q '[:find ?pa ?pn
                                                 :in $ ?client
                                                 :where [?pi :plaid-item/client ?client]
                                                 [?pi :plaid-item/accounts ?pa]
                                                 [?pa :plaid-account/name ?pn]]
                                               (dc/db conn)
                                               client-id))})
                          [:svg {":data-jdenticon-value" "plaidAccount" :width "24" :height "24"
                                 :x-init "$watch('plaidAccount', () => jdenticon())"}]])))

(defn- yodlee-account-select [client-id]
  (list
   (fc/with-field :bank-account/yodlee-account
     (com/validated-field {:errors (fc/field-errors)
                           :label "Yodlee account"
                           :class "w-[20em]"}
                          (com/select {:name        (fc/field-name)
                                       :allow-blank? true
                                       :error?      (fc/error?)
                                       :class "w-full"
                                       :value       (fc/field-value)
                                       :options
                                       (when client-id
                                         (dc/q '[:find ?pa ?pn2
                                                 :in $ ?client
                                                 :where [?pi :yodlee-provider-account/client ?client]
                                                 [?pi :yodlee-provider-account/accounts ?pa]
                                                 [?pa :yodlee-account/name ?pn]
                                                 [?pa :yodlee-account/number ?num]
                                                 [(str ?pn " - " ?num) ?pn2]]
                                               (dc/db conn)
                                               client-id))})))
   (fc/with-field :bank-account/use-date-instead-of-post-date?
     (com/checkbox {:name (fc/field-name)
                    :checked (fc/field-value)}
                   "(Yodlee only) use date instead of post date"))))

(defn- intuit-account-select [client-id]
  (list
   (fc/with-field :bank-account/intuit-bank-account
     (com/validated-field {:errors (fc/field-errors)
                           :label "Intuit account"
                           :class "w-[20em]"}
                          (com/select {:name        (fc/field-name)
                                       :allow-blank? true
                                       :error?      (fc/error?)
                                       :class "w-full"
                                       :value       (fc/field-value)
                                       :options
                                       (sort-by second (dc/q '[:find ?ia ?inn
                                                               :in $
                                                               :where [?ia :intuit-bank-account/name ?inn]]
                                                             (dc/db conn)))})))
   (fc/with-field :bank-account/use-date-instead-of-post-date?
     (com/checkbox {:name (fc/field-name)
                    :checked (fc/field-value)}
                   "(Yodlee only) use date instead of post date"))))
(defmethod bank-account-form
  :bank-account-type/credit [bank-account]
  [:div
   [:h2.text-lg
    (if (:new @bank-account)
      "New Credit Card Account"
      (str "Edit Credit Card: " (:bank-account/name @bank-account)))]

   (fc/with-field :db/id
     (com/hidden {:name (fc/field-name)
                  :value (fc/field-value)}))
   (fc/with-field :bank-account/type
     (com/hidden {:name (fc/field-name)
                  :value (name (fc/field-value))}))
   [:div.flex.space-x-2
    (fc/with-field :bank-account/name
      (com/validated-field {:errors (fc/field-errors)
                            :label "Nickname"
                            :class "w-[20em]"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :autofocus true
                                            :placeholder "BofA Checking"
                                            :class       "w-full"
                                            :value       (fc/field-value)})))
    (fc/with-field :bank-account/code
      (com/validated-field {:errors (fc/field-errors)
                            :label "Code"
                            :class "w-20"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :disabled (not (:new? @bank-account))
                                            :placeholder "NGOM-CASH"
                                            :class       "w-full"
                                            :value       (fc/field-value)})
                           (when-not (:new? @bank-account)
                             (com/hidden {:name (fc/field-name)
                                          :value (fc/field-value)}))))]
   (fc/with-field :bank-account/numeric-code
     (com/validated-field {:errors (fc/field-errors)
                           :label "Financial code"}
                          [:div {:class "w-[5em]"}
                           (com/int-input {:name        (fc/field-name)
                                           :error?      (fc/error?)
                                           :placeholder "11101"
                                           :class       "w-[5em]"
                                           :value       (fc/field-value)})]))

   (fc/with-field :bank-account/start-date
     [:div.flex.space-x-2.items-center
      (com/validated-field {:errors (fc/field-errors)
                            :label "Start Date"}
                           [:div {:class "w-[7em]"}
                            (com/date-input {:name        (fc/field-name)
                                             :error?      (fc/error?)
                                             :placeholder "12/01/2023"
                                             :hx-get (bidi/path-for ssr-routes/only-routes
                                                                    ::indicators/days-ago)
                                             :hx-trigger "change"
                                             :hx-target "#days-indicator"
                                             :hx-vals "js:{date: event.target.value}"
                                             :hx-swap "innerHTML"
                                             :class       "w-[5em]"
                                             :value       (some-> (fc/field-value)
                                                                  (clj-time.coerce/to-date-time)
                                                                 ;; todo do date coercion in the input
                                                                  (atime/unparse-local atime/normal-date))})])
      [:div#days-indicator
       (i/days-ago* (some-> (fc/field-value)))]])


   (fc/with-field :bank-account/include-in-reports
     (com/checkbox {:name (fc/field-name)
                    :value (boolean (fc/field-value))
                    :checked (fc/field-value)}
                   "Include in reports"))

   [:div
    (fc/with-field :bank-account/visible
      (com/checkbox {:name (fc/field-name)
                     :value (boolean (fc/field-value))
                     :checked (fc/field-value)}
                    "Visible for payment"))]

   [:h2.text-lg "Bank details"]
   (fc/with-field :bank-account/bank-name
     (com/validated-field {:errors (fc/field-errors)
                           :label "Bank Name"
                           :class "w-[20em]"}
                          (com/text-input {:name        (fc/field-name)
                                           :error?      (fc/error?)
                                           :placeholder "Bank of America"
                                           :value       (fc/field-value)})))

   [:div.flex.gap-2
    (fc/with-field :bank-account/number
      (com/validated-field {:errors (fc/field-errors)
                            :label "Account #"
                            :class "w-[10em]"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :class "w-full"
                                            :placeholder "1820190122"
                                            :value       (fc/field-value)})))]

   [:h2.text-lg "Integration details"]
   (plaid-account-select (:db/id (:snapshot fc/*form-data*)))
   (yodlee-account-select (:db/id (:snapshot fc/*form-data*)))
   (intuit-account-select (:db/id (:snapshot fc/*form-data*)))])

(defmethod bank-account-form
  :bank-account-type/check [bank-account]
  [:div
   [:h2.text-lg
    (if (:new @bank-account)
      "New Checking Account"

      (str "Edit Checking Account: " (:bank-account/name @bank-account)))]
   (fc/with-field :db/id
     (com/hidden {:name (fc/field-name)
                  :value (fc/field-value)}))
   (fc/with-field :bank-account/type
     (com/hidden {:name (fc/field-name)
                  :value (name (fc/field-value))}))
   [:div.flex.space-x-2
    (fc/with-field :bank-account/name
      (com/validated-field {:errors (fc/field-errors)
                            :label "Nickname"
                            :class "w-[20em]"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :autofocus true
                                            :placeholder "BofA Checking"
                                            :class       "w-full"
                                            :value       (fc/field-value)})))
    (fc/with-field :bank-account/code
      (com/validated-field {:errors (fc/field-errors)
                            :label "Code"
                            :class "w-20"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :disabled (not (:new? @bank-account))
                                            :placeholder "NGOM-CASH"
                                            :class       "w-full"
                                            :value       (fc/field-value)})
                           (when-not (:new? @bank-account)
                             (com/hidden {:name (fc/field-name)
                                          :value (fc/field-value)}))))]
   (fc/with-field :bank-account/numeric-code
     (com/validated-field {:errors (fc/field-errors)
                           :label "Financial code"}
                          [:div {:class "w-[5em]"}
                           (com/int-input {:name        (fc/field-name)
                                           :error?      (fc/error?)
                                           :placeholder "11101"
                                           :class       "w-[5em]"
                                           :value       (fc/field-value)})]))

   (fc/with-field :bank-account/start-date
     [:div.flex.space-x-2.items-center
      (com/validated-field {:errors (fc/field-errors)
                            :label "Start Date"}
                           [:div {:class "w-[7em]"}
                            (com/date-input {:name        (fc/field-name)
                                             :error?      (fc/error?)
                                             :placeholder "12/01/2023"
                                             :hx-get (bidi/path-for ssr-routes/only-routes
                                                                    ::indicators/days-ago)
                                             :hx-trigger "change"
                                             :hx-target "#days-indicator"
                                             :hx-vals "js:{date: event.target.value}"
                                             :hx-swap "innerHTML"
                                             :class       "w-[5em]"
                                             :value       (some-> (fc/field-value)
                                                                  (clj-time.coerce/to-date-time)
                                                                 ;; todo do date coercion in the input
                                                                  (atime/unparse-local atime/normal-date))})])
      [:div#days-indicator
       (i/days-ago* (some-> (fc/field-value)))]])

   (fc/with-field :bank-account/include-in-reports
     (com/checkbox {:name (fc/field-name)
                    :value (boolean (fc/field-value))
                    :checked (fc/field-value)}
                   "Include in reports"))

   [:div
    (fc/with-field :bank-account/visible
      (com/checkbox {:name (fc/field-name)
                     :value (boolean (fc/field-value))
                     :checked (fc/field-value)}
                    "Visible for payment"))]

   [:h2.text-lg "Bank details"]
   (fc/with-field :bank-account/bank-name
     (com/validated-field {:errors (fc/field-errors)
                           :label "Bank Name"
                           :class "w-[20em]"}
                          (com/text-input {:name        (fc/field-name)
                                           :error?      (fc/error?)
                                           :placeholder "Bank of America"
                                           :value       (fc/field-value)})))

   [:div.flex.gap-2
    (fc/with-field :bank-account/number
      (com/validated-field {:errors (fc/field-errors)
                            :label "Account #"
                            :class "w-[10em]"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :class "w-full"
                                            :placeholder "1820190122"
                                            :value       (fc/field-value)})))
    (fc/with-field :bank-account/routing
      (com/validated-field {:errors (fc/field-errors)
                            :label "Routing #"
                            :class "w-[8em]"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :class "w-full"
                                            :placeholder "1238912"
                                            :value       (fc/field-value)})))
    (fc/with-field :bank-account/bank-code
      (com/validated-field {:errors (fc/field-errors)
                            :label "Bank Code"
                            :class "w-[8em]"}
                           (com/text-input {:name        (fc/field-name)
                                            :error?      (fc/error?)
                                            :class "w-full"
                                            :placeholder "12/10123"
                                            :value       (fc/field-value)})))]

   (fc/with-field :bank-account/check-number
     (com/validated-field {:errors (fc/field-errors)
                           :label "Check Number"
                           :class "w-[8em]"}
                          (com/text-input {:name        (fc/field-name)
                                           :error?      (fc/error?)
                                           :class "w-full"
                                           :placeholder "50000"
                                           :value       (fc/field-value)})))
   [:h2.text-lg "Integration details"]
   (plaid-account-select (:db/id (:snapshot fc/*form-data*)))
   (yodlee-account-select (:db/id (:snapshot fc/*form-data*)))
   (intuit-account-select (:db/id (:snapshot fc/*form-data*)))])



(defn new-bank-account-card []
  [:div {:class "w-[30em]"}
   (com/card {:class "w-full border-dotted bg-gray-50"}
             [:div.flex.justify-center.items-center.h-16
              [:div [:span "Add a new "
                     (com/link {:hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/navigate)
                                                {:to (mm/encode-step-key :new-bank-account)
                                                 :from (mm/encode-step-key :bank-accounts)
                                                 :bank-account-type "cash"})} "cash account")
                     ", "
                     (com/link {:hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/navigate)
                                                {:to (mm/encode-step-key :new-bank-account)
                                                 :from (mm/encode-step-key :bank-accounts)
                                                 :bank-account-type "credit"})} "credit card")
                     ", "
                     (com/link {:hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/navigate)
                                                {:to (mm/encode-step-key :new-bank-account)
                                                 :from (mm/encode-step-key :bank-accounts)
                                                 :bank-account-type "check"})} "checking account")]]])])

(defrecord BankAccountsModal [linear-wizard]
  mm/ModalWizardStep
  (step-name [_]
    "Bank Accounts")
  (step-key [_]
    :bank-accounts)

  (edit-path [_ _] [])


  (step-schema [_]
    (mut/select-keys (mm/form-schema linear-wizard) #{}))

  (render-step
    [this _]
    (mm/default-render-step
     linear-wizard this
     :head (dialog-header this)
     :body (mm/default-step-body {}

                                 [:div#bank-account-list {:hx-put (bidi/path-for ssr-routes/only-routes ::route/sort-bank-accounts)
                                                          :hx-trigger "end"}
                                  (fc/with-field :client/bank-accounts
                                    (com/validated-field
                                     {:errors (fc/field-errors)
                                      :label  "Bank Accounts"}
                                     [:div.flex.flex-col.space-y-4.sortable
                                      (fc/cursor-map (fn [ba-cursor]
                                                       (when (:bank-account/type @ba-cursor)
                                                         (bank-account-card ba-cursor))))

                                      (new-bank-account-card)]))])
     :footer
     [:fieldset {}
      (mm/default-step-footer linear-wizard this
                              :validation-route ::route/navigate)]
     :validation-route ::route/navigate)))


(defn square-location-table []
  [:div#square-locations
   [:div.htmx-indicator
    "Loading..."]
   [:div.htmx-indicator-hidden
    [:table
     [:thead
      [:tr
       [:td "Square location"]
       [:td "Client location"]]]
     [:tbody
      (fc/cursor-map (fn [square-location]
                       [:tr
                        (fc/with-field :db/id
                          (com/hidden {:name (fc/field-name)
                                       :value (fc/field-value)}))
                        (fc/with-field :square-location/name
                          (com/hidden {:name (fc/field-name)
                                       :value (fc/field-value)}))
                        (fc/with-field :square-location/square-id
                          (com/hidden {:name (fc/field-name)
                                       :value (fc/field-value)}))
                        [:td (:square-location/name @square-location)]
                        [:td (fc/with-field :square-location/client-location
                               (com/text-input {:name (fc/field-name)
                                                :value (fc/field-value)}))]]))]]]])

(defn refresh-square-locations [request]
  (let [locations @(de/timeout!
                    (de/chain (square/client-locations {:client/square-auth-token (get-in request [:query-params (keyword "step-params[client/square-auth-token]")])})
                              (fn [client-locations]
                                (into []
                                      (for [square-location client-locations]
                                        {:db/id                     (str (java.util.UUID/randomUUID))
                                         :square-location/name      (:name square-location)
                                         :square-location/square-id (:id square-location)}))))
                    2000
                    :not-found)]
    (html-response
     (if (= locations :not-found)
       [:div#square-locations
        "No locations found."]
       (fc/start-form-with-prefix
        [:step-params :client/square-locations]
        locations
        []
        (square-location-table))))))

(defrecord IntegrationsModal [linear-wizard]
  mm/ModalWizardStep
  (step-name [_]
    "Integrations")
  (step-key [_]
    :integrations)

  (edit-path [_ _] [])

  (step-schema [_]
    (mut/select-keys (mm/form-schema linear-wizard) #{}))

  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head (dialog-header this)
     :body (mm/default-step-body
            {}
            [:div
             [:div.flex.gap-2.items-center
              (fc/with-field :client/square-auth-token
                (com/validated-field
                 {:errors (fc/field-errors)
                  :label  "Square Auth Token"}
                 (com/text-input {:name        (fc/field-name)
                                  :id "square-token"
                                  :error?      (fc/error?)
                                  :placeholder "Token from square"
                                  :class       "w-64"
                                  :value       (fc/field-value)})))
              (com/button {:hx-get (bidi/path-for ssr-routes/only-routes ::route/refresh-square-locations)
                           :hx-include "#square-token"
                           :hx-trigger "click"
                           :hx-indicator "#square-locations"
                           :hx-target "#square-locations" }
                          "Refresh")]

             (fc/with-field :client/square-locations
               (square-location-table))])
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate)))

(defrecord BankAccountModal [linear-wizard which]
  mm/ModalWizardStep
  (step-name [_]
    "Bank Accounts")
  (step-key [_]
    [:bank-account which])

  (edit-path [_ request]
    (let [account-index (->> (:client/bank-accounts (:snapshot (:multi-form-state request)))
                             (map vector (range))
                             (filter (fn [[_ ba]]
                                       (= (:db/id ba)
                                          which)))
                             ffirst)]

      [:client/bank-accounts (or account-index
                                 (count (:client/bank-accounts (:snapshot (:multi-form-state request)))))]))

  (step-schema [_]
    bank-account-schema)

  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head (dialog-header this)
     :body (mm/default-step-body {}
                                 [:div {:class "htmx-added:opacity-0 opacity-100 transition-opacity duration-300"}
                                  (fc/with-field :new?
                                    (when (fc/field-value)
                                      (com/hidden {:name (fc/field-name)
                                                   :value "true"})))
                                  (bank-account-form fc/*current*)])

     :footer
     (mm/default-step-footer linear-wizard this
                             :next-button
                             (com/button {:hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/navigate)
                                                          {:from (mm/encode-step-key [:bank-account which])
                                                           :to (mm/encode-step-key :bank-accounts)})}
                                         ;; todo maybe make a helper for progress urls
                                         "Accept")
                             :discard-button
                             (com/a-button {:hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/discard)
                                                            {:from (mm/encode-step-key [:bank-account which])
                                                             :to (mm/encode-step-key :bank-accounts)})}
                                         ;; todo maybe make a helper for progress urls
                                           "discard"))
     :validation-route ::route/navigate))
  mm/Initializable
  (init-step-params
    [_ multi-form-state request]
    (let [bank-account-type (get-in request [:query-params :bank-account-type])]
      (if (= {} (:step-params multi-form-state))
        (cond->
         {:db/id (str (java.util.UUID/randomUUID))
          :new? true}

          bank-account-type (assoc :bank-account/type (keyword "bank-account-type" bank-account-type)
                                   :bank-account/visible true))
        (:step-params multi-form-state))))

  mm/Discardable
  (can-discard? [_ step-params]
    (:new? step-params))
  (discard-changes [_ multi-form-state]
    (-> multi-form-state
        (update-in [:snapshot :client/bank-accounts]
                   (fn [bank-accounts]
                     (filterv #(not= (get-in multi-form-state [:step-params :db/id]) (:db/id %)) bank-accounts)))
        (mm/select-state [] nil))))



(defrecord CashFlowModal [linear-wizard]
  mm/ModalWizardStep
  (step-name [_]
    "Cash Flow")

  (step-key [_]
    :cash-flow)

  (edit-path [_ _] [])

  (step-schema [_]
    (mut/select-keys (mm/form-schema linear-wizard) #{:client/week-a-credits :client/week-a-debits :client/week-b-credits :client/week-b-debits}))

  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head (dialog-header this)
     :body (mm/default-step-body {}
                                 [:div.flex.space-x-4
                                  (fc/with-field :client/week-a-credits
                                    (com/validated-field {:errors (fc/field-errors)
                                                          :label "Week A Credits"
                                                          :class "w-32"}
                                                         (com/text-input {:name        (fc/field-name)
                                                                          :error?      (fc/error?)
                                                                          :placeholder "123.33"
                                                                          :class       "w-32"
                                                                          :value       (fc/field-value)})))
                                  (fc/with-field :client/week-a-debits
                                    (com/validated-field {:errors (fc/field-errors)
                                                          :label "Week A Debits"}
                                                         (com/text-input {:name        (fc/field-name)
                                                                          :error?      (fc/error?)
                                                                          :placeholder "123.33"
                                                                          :class       "w-32"
                                                                          :value       (fc/field-value)})))]
                                 [:div.flex.space-x-4
                                  (fc/with-field :client/week-b-credits
                                    (com/validated-field {:errors (fc/field-errors)
                                                          :label "Week B Credits"
                                                          :class "w-32"}
                                                         (com/text-input {:name        (fc/field-name)
                                                                          :error?      (fc/error?)
                                                                          :placeholder "123.33"
                                                                          :class       "w-32"
                                                                          :value       (fc/field-value)})))
                                  (fc/with-field :client/week-b-debits
                                    (com/validated-field {:errors (fc/field-errors)
                                                          :label "Week B Debits"}
                                                         (com/text-input {:name        (fc/field-name)
                                                                          :error?      (fc/error?)
                                                                          :placeholder "123.33"
                                                                          :class       "w-32"
                                                                          :value       (fc/field-value)})))]
                                 [:div])
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate)))

(defrecord OtherSettingsModal [linear-wizard]
  mm/ModalWizardStep
  (step-name [_]
    "Other Settings")

  (step-key [_]
    :other-settings)

  (edit-path [_ _] [])

  (step-schema [_]
    (mut/select-keys (mm/form-schema linear-wizard) #{:client/feature-flags :client/groups}))

  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head (dialog-header this)
     :body (mm/default-step-body {}
                                 (fc/with-field :client/feature-flags
                                   (com/validated-field
                                    {:errors (fc/field-errors)
                                     :label  "Feature Flags"}
                                    (com/data-grid {:headers [(com/data-grid-header {} "Flag")
                                                              (com/data-grid-header {:class "w-16"})]}
                                                   (fc/cursor-map #(feature-flag-row %))
                                                   (com/data-grid-new-row {:colspan 2
                                                                           :hx-get  (bidi/path-for ssr-routes/only-routes ::route/new-feature-flag)
                                                                           :index   (count (fc/field-value))}
                                                                          "New flag"))))
                                 (fc/with-field :client/groups
                                   (com/validated-field
                                    {:errors (fc/field-errors)
                                     :label  "Groups"}
                                    (com/data-grid {:headers [(com/data-grid-header {} "Group")
                                                              (com/data-grid-header {:class "w-16"})]}
                                                   (fc/cursor-map #(group-row %))
                                                   (com/data-grid-new-row {:colspan 2
                                                                           :hx-get  (bidi/path-for ssr-routes/only-routes ::route/new-group)
                                                                           :index   (count (fc/field-value))}

                                                                          "New group")))))
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate)))

(defrecord ClientWizard [form-params current-step entity]
  mm/LinearModalWizard
  (hydrate-from-request [this request]
    (assoc this :entity (:entity request)))
  (navigate [this step-key]
    (assoc this :current-step step-key))
  (get-current-step [this]
    (if current-step
      (mm/get-step this current-step)
      (mm/get-step this :info)))
  (render-wizard [this {:keys [multi-form-state] :as request}]
    (mm/default-render-wizard
     this request
     :form-params
     (-> mm/default-form-props
         (assoc (if (get-in multi-form-state [:snapshot :db/id])
                  :hx-put
                  :hx-post)
                (str (bidi/path-for ssr-routes/only-routes ::route/save))
                :x-data (hx/json {"clientName"  (:client/name (:step-params multi-form-state))})))))
  (steps [_]
    [:info
     :matches
     :contact
     :bank-accounts
     :integrations
     :cash-flow
     :other-settings])

  (get-step [this step-key]
    (let [step-key-result (mc/parse  mm/step-key-schema step-key)
          [step-key-type step-key] step-key-result]
      (if (= :step step-key-type)
        (get {:info (->InfoModal this)
              :matches (->MatchesModal this)
              :contact (->ContactModal this)
              :bank-accounts (->BankAccountsModal this)
              :integrations (->IntegrationsModal this)
              :cash-flow (->CashFlowModal this)
              :other-settings (->OtherSettingsModal this)
              :new-bank-account (->BankAccountModal this "new")}
             step-key)

        (get {:bank-account (->BankAccountModal this (second step-key))}
             (first step-key)))))
  (form-schema [_] form-schema-2)
  (submit [_ {:keys [multi-form-state request-method identity] :as request}]
    (let [snapshot (mc/decode
                    form-schema-2
                    (:snapshot multi-form-state)
                    mt/strip-extra-keys-transformer)
          entity (cond-> snapshot
                   (= :post request-method) (assoc :db/id "new")
                   (= :put request-method) (dissoc :client/code)

                   (:client/locked-until snapshot) (update :client/locked-until clj-time.coerce/to-date)
                   (seq (:client/groups snapshot)) (update :client/groups #(mapv str/upper-case %))
                   (seq (:client/bank-accounts snapshot)) (update :client/bank-accounts
                                                                  (fn [bank-accounts]
                                                                    (mapv
                                                                     (fn [bank-account]
                                                                       (-> bank-account
                                                                           (update :bank-account/start-date #(when % (clj-time.coerce/to-date %)))))
                                                                     bank-accounts))))
          _ (alog/info ::peeker :entity (:client/bank-accounts entity))
          _ (when (and (:client/code entity) (pull-id (dc/db conn)  [:client/code (:client/code entity)]))
              (form-validation-error (format "The code '%s' is already in use" (:client/code entity))
                                     :code (:client/code entity)))

          {:keys [tempids]} (audit-transact [[:upsert-entity entity]]
                                            (:identity request))

          updated-client   (dc/pull (dc/db conn)
                                    default-read
                                    (or (get tempids (:db/id entity)) (:db/id entity)))]
      (when (:client/name updated-client)
        (solr/index-documents-raw solr/impl "clients"
                                  [{"id"    (:db/id updated-client)
                                    "name"  (conj (or (:client/matches updated-client) [])
                                                  (:client/name updated-client))
                                    "code"  (:client/code updated-client)

                                    "exact" (map str/upper-case (conj (or (:client/matches updated-client) [])
                                                                      (:client/name updated-client)))}]))
      (html-response
       (row* identity updated-client {:flash? true})
       :headers (cond-> {"hx-trigger"  "modalclose"}
                  (= :post request-method) (assoc "hx-retarget" "#entity-table tbody"
                                                  "hx-reswap" "afterbegin")
                  (= :put request-method)  (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-client))
                                                  "hx-reswap" "outerHTML"))))))

(def client-wizard
  (->ClientWizard nil nil nil))

(defn sort-bank-accounts [{:keys [multi-form-state wizard] :as request}]
  (let [sort-index (into {} (map vector (:item (:form-params request)) (range)))
        new-bank-accounts (->> multi-form-state
                               :snapshot
                               :client/bank-accounts
                               (map (fn [bank-account]
                                      (assoc bank-account :bank-account/sort-order (sort-index (:db/id bank-account)))))
                               (sort-by :bank-account/sort-order)
                               (into []))]
    (html-response
     (mm/render-wizard wizard (update request :multi-form-state
                                      (comp
                                       #(mm/select-state % [] {})
                                       #(assoc-in % [:snapshot :client/bank-accounts] new-bank-accounts)))))))


(def sales-summary-query
  "[:find ?d4 (sum ?total) (sum ?tax) (sum ?tip)  (sum ?service-charge) (sum ?discount) (sum ?returns)
  :with ?s
 :in $
 :where
 [(ground (iol-ion.query/recent-date 120)) ?min-d]
  [(ground #inst \"2040-01-01\") ?max-d]
 [?c :client/code \"%s\"]
 [(iol-ion.query/sales-orders-in-range $ ?c ?min-d ?max-d) [?s ...]]
 [?s :sales-order/date ?d]
 [?s :sales-order/total ?total]
 [?s :sales-order/tax ?tax]
 [?s :sales-order/tip ?tip]
 [?s :sales-order/service-charge ?service-charge]
 [?s :sales-order/returns ?returns]
 [?s :sales-order/discount ?discount]
 [(iol-ion.query/excel-date ?d) ?d4]
 ]")

(def sales-category-query
  "[:find  ?d4 ?n ?n2 (sum ?total) (sum ?tax) (sum ?discount)
   :with ?s ?li
   :in $ 
   :where 
   [(ground (iol-ion.query/recent-date 120)) ?min-d]
   [(ground #inst \"2040-01-01\") ?max-d]
   [?c :client/code \"%s\"]
   [(iol-ion.query/sales-orders-in-range $ ?c ?min-d ?max-d) [?s ...]]
   [?s :sales-order/date ?d]
   [?s :sales-order/line-items ?li]
   [?li :order-line-item/category ?n]
   [(get-else $ ?li :order-line-item/item-name \"\") ?n2]
   [?li :order-line-item/total ?total]
   [?li :order-line-item/tax ?tax]
   [?li :order-line-item/discount ?discount]
   [(iol-ion.query/excel-date ?d) ?d4]]")

(def expected-deposits-query
  "[:find ?d4 ?t ?f
:in $ 
:where 
[(ground (iol-ion.query/recent-date 120)) ?min-d]
  [?c :client/code \"%s\"]
[?s :expected-deposit/client ?c]
[?s :expected-deposit/sales-date ?date]
[(>= ?date ?min-d)]
[?s :expected-deposit/total ?t]
[?s :expected-deposit/fee ?f]
[(iol-ion.query/excel-date ?date) ?d4]
]")

(def tenders-query
  "[:find ?d4 ?type ?p2 (sum ?total) (sum ?tip)
:with ?charge
:in $
:where
[(ground (iol-ion.query/recent-date 120)) ?min-d]
[?c :client/code \"%s\"]
[?s :sales-order/client ?c]
[?s :sales-order/date ?date]
[(>= ?date ?min-d)]
[?s :sales-order/charges ?charge]
[?charge :charge/type-name ?type]
[?charge :charge/total ?total]
[?charge :charge/tip ?tip]
[(get-else $ ?charge :charge/processor :na) ?ccp]
[(get-else $ ?ccp :db/ident :na) ?p]
[(name ?p) ?p2]
[(iol-ion.query/excel-date ?date) ?d4]
]")

(def tenders2-query
  "[:find ?d4 ?type ?p2 (sum ?total) (sum ?tip)
   :with ?charge
   :in $
   :where
   [(ground (iol-ion.query/recent-date 120)) ?min-d]
   [?charge :charge/date ?date]
   [(>= ?date ?min-d)]
   [?charge :charge/client ?c]
   [?c :client/code \"%s\"]
   [?charge :charge/type-name ?type]
   [?charge :charge/total ?total]
   [?charge :charge/tip ?tip]
   (or

     (and [_ :expected-deposit/charges ?charge ]
          [(ground :settlement) ?ccp]
          [(ground :settlement) ?p])
     (and 
       (not [_ :expected-deposit/charges ?charge])
       [(get-else $ ?charge :charge/processor :na) ?ccp]
       [(get-else $ ?ccp :db/ident :na) ?p]
       ))
   [(name ?p) ?p2]
   [(iol-ion.query/excel-date ?date) ?d4]]
")

(def refunds-query
  "[:find ?d4 ?t (sum ?total) (sum ?fee)
:with ?r
:in $ 
:where 
   [(ground (iol-ion.query/recent-date 120)) ?min-d]
  [?r :sales-refund/client [:client/code \"%s\"]]
[?r :sales-refund/date ?date]
[(>= ?date ?min-d)]
[?r :sales-refund/total ?total]
[?r :sales-refund/fee ?fee]
[?r :sales-refund/type ?t]
[(iol-ion.query/excel-date ?date) ?d4]
]")

(def cash-drawer-shift-query
  "[:find ?d4 (sum ?paid-in) (sum ?paid-out) (sum ?expected-cash) (sum ?opened-cash)
:with ?cds
:in $ 
:where 
[?cds :cash-drawer-shift/date ?date]
[(ground (iol-ion.query/recent-date 120)) ?min-d]
[(>= ?date ?min-d)]
[?cds :cash-drawer-shift/client [:client/code \"%s\"]]
[?cds :cash-drawer-shift/paid-in ?paid-in]
[?cds :cash-drawer-shift/paid-out ?paid-out]
[?cds :cash-drawer-shift/expected-cash ?expected-cash]
[?cds :cash-drawer-shift/opened-cash ?opened-cash]
[(iol-ion.query/excel-date ?date) ?d4]]")




(defn setup-sales-queries-impl [client-id]
  (let [{client-code :client/code feature-flags :client/feature-flags} (dc/pull (dc/db conn) '[:client/code :client/feature-flags] client-id)
        is-new-square?                                                 ((set feature-flags) "new-square")]
    (q/put-query (str (UUID/randomUUID))
                 (format sales-summary-query client-code)
                 (str "sales query for " client-code)
                 (str client-code "-sales-summary")
                 [:client/code client-code])
    (q/put-query (str (UUID/randomUUID))
                 (format sales-category-query client-code)
                 (str "sales category query for " client-code)
                 (str client-code "-sales-category")
                 [:client/code client-code])
    (q/put-query (str (UUID/randomUUID))
                 (format expected-deposits-query client-code)
                 (str "expected deposit query for " client-code)
                 (str client-code "-expected-deposit")
                 [:client/code client-code])
    (q/put-query (str (UUID/randomUUID))
                 (format (if is-new-square? tenders2-query tenders-query) client-code)
                 (str "tender query for " client-code)
                 (str client-code "-tender")
                 [:client/code client-code])

    (q/put-query (str (UUID/randomUUID))
                 (format refunds-query client-code)
                 (str "refunds query for " client-code)
                 (str client-code "-refund")
                 [:client/code client-code])

    (q/put-query (str (UUID/randomUUID))
                 (format cash-drawer-shift-query client-code)
                 (str "cash drawer shift query for " client-code)
                 (str client-code "-cash-drawer-shift")
                 [:client/code client-code])))

(defn reset-all-queries []
  (doseq [[c] (dc/q '[:find ?c :where [?c :client/code]] (dc/db conn))]
    (setup-sales-queries-impl c)))

(defn copy-button [{:keys [which url]} & children]
  (com/button {"@click" (format "copyToClipboard(%s)"
                                (cheshire/generate-string (format (slurp (io/resource which)) url)))}
              children))


(defn biweekly-sales-powerquery [request]
  (setup-sales-queries-impl (:db/id (:route-params request)))
  (modal-response
   (com/modal  {}
               (com/modal-card-advanced
                {}
                (com/modal-header {} [:div.m-2 "Sales exports"])
                (com/modal-body {}
                                (let [client-code (pull-attr (dc/db conn) :client/code (:db/id (:route-params request)))
                                      sales-summary-id     (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-sales-summary")]))
                                      sales-category-id    (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-sales-category")]))
                                      expected-deposit-id  (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-expected-deposit")]))
                                      tender-id            (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-tender")]))
                                      refund-id            (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-refund")]))
                                      cash-drawer-shift-id (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-cash-drawer-shift")]))]
                                  [:div.flex.flex-col.gap-2
                                   (copy-button {:which "powerqueries/sales_summary.txt" :url sales-summary-id}
                                                "Copy Sales Order Summary Power Query")
                                   (copy-button {:which "powerqueries/sales_category.txt" :url sales-category-id}
                                                "Copy Sales Category Power Query")
                                   (copy-button {:which "powerqueries/tenders.txt" :url tender-id}
                                                "Copy Tenders Power Query")
                                   (copy-button {:which "powerqueries/expected_deposits.txt" :url expected-deposit-id}
                                                "Copy Expected Deposits Power Query")
                                   (copy-button {:which "powerqueries/refunds.txt" :url refund-id}
                                                "Copy Refunds Power Query")
                                   #_(copy-button {:which "powerqueries/cash_drawer_shift.txt" :url cash-drawer-shift-id}
                                                  "Copy Cash Drawer Shift Power Query")]))

                (com/modal-footer {} [:div])))))


(def key->handler
  (apply-middleware-to-all-handlers
   {::route/page              (helper/page-route grid-page)
    ::route/table             (helper/table-route grid-page)
    ::route/new-location       (add-new-primitive-handler [:step-params :client/locations]
                                                          ""
                                                          location-row)
    ::route/new-feature-flag       (add-new-primitive-handler [:step-params :client/feature-flags]
                                                              ""
                                                              feature-flag-row)
    ::route/new-match       (add-new-primitive-handler [:step-params :client/matches]
                                                       ""
                                                       match-row)
    ::route/new-group       (add-new-primitive-handler [:step-params :client/groups]
                                                       ""
                                                       group-row)
    ::route/new-location-match (add-new-entity-handler [:step-params :client/location-matches]
                                                       (fn [cursor _]  (location-match-row cursor)))
    ::route/new-email-contact       (add-new-entity-handler [:step-params :client/emails]
                                                            (fn [cursor _] (email-contact-row cursor)))
    ::route/save              (-> mm/submit-handler
                                  (mm/wrap-wizard client-wizard)
                                  (mm/wrap-decode-multi-form-state)
                                  (wrap-entity [:form-params :db/id] default-read))
    ::route/biweekly-sales-powerquery
    (-> biweekly-sales-powerquery
        (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
    ::route/refresh-square-locations
    refresh-square-locations
    ::route/navigate
    (-> mm/next-handler
        (mm/wrap-wizard client-wizard)
        (mm/wrap-decode-multi-form-state)
        (wrap-entity [:route-params :db/id] default-read)
        (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
    ::route/sort-bank-accounts
    (-> sort-bank-accounts
        (wrap-schema-enforce :form-schema [:map [:item [:vector entity-id]]])
        (mm/wrap-wizard client-wizard)
        (mm/wrap-decode-multi-form-state))
    ::route/discard
    (-> mm/discard-handler
        (mm/wrap-wizard client-wizard)
        (mm/wrap-decode-multi-form-state))
    ::route/edit-dialog (-> mm/open-wizard-handler
                            (mm/wrap-wizard client-wizard)
                            (mm/wrap-init-multi-form-state (fn [request]
                                                             (let [sorted (-> (:entity request)
                                                                              (update :client/bank-accounts
                                                                                      (fn [bas]
                                                                                        (into [] (sort-by :bank-account/sort-order bas)))))]
                                                               (mm/->MultiStepFormState sorted
                                                                                        []
                                                                                        sorted))))
                            (wrap-entity [:route-params :db/id] default-read)
                            (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
    ::route/new-dialog  (-> mm/open-wizard-handler
                            (mm/wrap-init-multi-form-state (fn [_]
                                                             (mm/->MultiStepFormState {}
                                                                                      []
                                                                                      {})))
                            (mm/wrap-wizard client-wizard))}
   (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)
         (wrap-admin)
         (wrap-client-redirect-unauthenticated)))))
