(ns auto-ap.ssr.admin.vendors
  (:require
   [auto-ap.cursor :as cursor]
   [auto-ap.datomic
             :refer [add-sorter-fields apply-pagination apply-sort-3
                     audit-transact audit-transact-batch audit-transact-batch
                     conn merge-query pull-attr pull-many query2]]
   [auto-ap.datomic.accounts :as d-accounts]
   [auto-ap.logging :as alog]
   [auto-ap.query-params :refer [wrap-copy-qp-pqp]]
   [auto-ap.routes.admin.vendors :as route]
   [auto-ap.routes.utils
             :refer [wrap-admin wrap-client-redirect-unauthenticated]]
   [auto-ap.solr :as solr]
   [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.components.timeline :as timeline]
   [auto-ap.ssr.form-cursor :as fc]
   [auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
   [auto-ap.ssr.hx :as hx]
   [auto-ap.ssr.svg :as svg]
   [auto-ap.ssr.utils
             :refer [apply-middleware-to-all-handlers
                     default-grid-fields-schema entity-id
                     form-validation-error html-response many-entity
                     modal-response ref->enum-schema ref->select-options strip
                     temp-id wrap-entity wrap-form-4xx-2 wrap-merge-prior-hx
                     wrap-schema-enforce]]
   [bidi.bidi :as bidi]
   [clojure.string :as str]
   [datomic.api :as dc]
   [malli.core :as mc]
   [malli.transform :as mt]
   [malli.util :as mut]))

(def query-schema (mc/schema
                   [:maybe
                    (into [:map {}
                           [:name {:optional true :default nil} [:maybe [:string {:string/decode strip}]]]
                           #_[:role {:optional true} [:maybe (ref->enum-schema "user-role")]]
                           #_[:client {:optional true :default nil} [:maybe  [:entity-map {:pull [:db/id :client/name]}]]] ]
                          default-grid-fields-schema)]))
(defn filters [request]
  [:form {"hx-trigger"         "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
          "hx-get"             (bidi/path-for ssr-routes/only-routes
                                              ::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 "Cash"
                                :size        :small}))
    (com/field {:label "Type"}
               (com/radio-card {:size    :small
                           :name    "type"
                           :value (:type (:query-params request))
                           :options [{:value   ""
                                      :content "All"}
                                     {:value   "only-hidden"
                                      :content "Only hidden"}
                                     {:value   "only-global"
                                      :content "Only global"}
                                     #_{:value   "potential-duplicates"
                                        :content "Potential duplicates"}]}))]])

(def default-read '[:db/id
                    :vendor/name
                    :vendor/print-as
                    :vendor/hidden
                    :vendor/terms
                    :vendor/legal-entity-tin
                    :vendor/legal-entity-name
                    :vendor/legal-entity-first-name
                    :vendor/legal-entity-middle-name
                    :vendor/legal-entity-last-name
                    {:vendor/primary-contact [:contact/email :contact/first-name :contact/middle-name :contact/last-name]
                     :vendor/address [:address/street1 :address/street2 :address/city :address/state :address/zip :db/id]
                     :vendor/terms-overrides [:vendor-terms-override/terms {:vendor-terms-override/client [:client/name :db/id]} :db/id]
                     :vendor/automatically-paid-when-due [:db/id :client/name]
                     :vendor/account-overrides [{:vendor-account-override/account [:account/name :db/id]
                                                 :vendor-account-override/client  [:client/name :db/id]}
                                                :db/id]
                     :vendor/default-account [:account/name :account/numeric-code :db/id]
                     [:vendor-usage/_vendor :as :vendor/usage] [:vendor-usage/client :vendor-usage/count]
                     [:vendor/legal-entity-1099-type :xform iol-ion.query/ident] [:db/ident]
                     [:vendor/legal-entity-tin-type :xform iol-ion.query/ident] [:db/ident]}])

(defn fetch-ids [db request]
  (let [query-params (:query-params request)
        query (cond-> {:query {:find  []
                               :in    '[$]
                               :where '[]}
                       :args  [db]}
                (:sort query-params) (add-sorter-fields {"name" ['[?e :vendor/name ?n]
                                                                 '[(clojure.string/upper-case ?n) ?sort-name]]}
                                                        query-params)
                (some->> query-params :name not-empty)
                (merge-query {:query {:find  []
                                      :in ['?ns]
                                      :where ['[?e :vendor/name ?an]
                                              '[(clojure.string/upper-case ?an) ?upper-an]
                                              '[(clojure.string/includes? ?upper-an ?ns)]]}
                              :args [(str/upper-case (:name query-params))]})

                (some->> query-params :type not-empty (= "only-hidden"))
                (merge-query {:query {:find  []
                                      :in    []
                                      :where ['[?e :vendor/hidden true]]}
                              :args  []})
                (some->> query-params :type not-empty (= "only-global"))
                (merge-query {:query {:find  []
                                      :in    []
                                      :where ['[?e :vendor/hidden false]]}
                              :args  []})

                true
                (merge-query {:query {:find ['?sort-default '?e]
                                      :where ['[?e :vendor/name ?un]
                                              '[(clojure.string/upper-case ?un) ?sort-default]]}}))]
    (cond->> (query2 query)
      true (apply-sort-3 query-params)
      true (apply-pagination query-params))))

(defn hydrate-results [ids db _]
  (let [results (->> (pull-many db default-read ids)
                     (group-by :db/id))
        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/merge))
                                                 :color  :secondary}
                                                "Merge")
                                    (com/button {:hx-get    (str (bidi/path-for ssr-routes/only-routes
                                                                                ::route/new))
                                                 :color     :primary}
                                                "New Vendor")])
                 :row-buttons (fn [_ entity]
                                [(com/icon-button {:hx-get (str (bidi/path-for ssr-routes/only-routes
                                                                               ::route/edit
                                                                               :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)}
                                       "Vendors"]]
                 :title       "Vendors"
                 :entity-name "Vendor"
                 :query-schema query-schema
                 :route       ::route/table
                 :headers     [{:key      "name"
                                :name     "Name"
                                :sort-key "name"
                                :render   (fn [entity]
                                            (let [client-usage (count (:vendor/usage entity))
                                                  total-usage (reduce + 0 (map :vendor-usage/count (:vendor/usage entity)))]
                                              [:div.flex.gap-x-2.flex-wrap
                                               [:div (:vendor/name entity)]
                                               [:div (if (= client-usage 0)
                                                       (com/pill {:color :red}
                                                                 "Unused")

                                                       (com/pill {:color :primary}
                                                                 (format "Used by %d clients" client-usage)))]
                                               (when (> client-usage 0)
                                                 [:div (com/pill {:color :secondary}
                                                                 (format "Used %d times" total-usage))])]))}
                               {:key      "email"
                                :name     "Email"
                                :sort-key "email"
                                :render   #(some-> % :vendor/primary-contact :contact/email)}
                               {:key      "default-account"
                                :name     "Default Account"
                                :sort-key "default-account"
                                :render   #(some-> % :vendor/default-account :account/name)}]}))

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



(defn merge-submit [{:keys [form-params request-method identity] :as request}]
  (if (= (:source-vendor form-params)
         (:target-vendor form-params))
    (form-validation-error "Please select two different vendors"
                           :form-params form-params))
  (let [transaction (->> (dc/q {:find  '[?x ?a2]
                                :in    '[$ ?vendor-from]
                                :where ['[?x ?a ?vendor-from]
                                        '[?a :db/ident ?a2]]}
                               (dc/db conn)
                               (:source-vendor form-params))
                         (mapcat (fn [[src attr]]

                                   [[:db/retract src attr (:source-vendor form-params)]
                                    [:db/add src attr (:target-vendor form-params)]])))]
    (alog/peek transaction)
    (audit-transact-batch transaction identity)
    (audit-transact [[:db/retractEntity (:source-vendor form-params)]] identity))
  (html-response
   [:div]
   :headers {"hx-trigger" (hx/json {"modalclose"   ""
                                    "notification" "Vendor merge successful."})}))

(defn back-button []
  [:a {"@click" "$dispatch('modalprevious')"
       "class"  "text-sm font-medium text-gray-700 cursor-pointer"}
   "Back"])

(defn timeline [{:keys [active]}]
  (let [steps ["Info" "Terms" "Account" "Address" "Legal"]
        active-index (.indexOf steps active)]
    (timeline/timeline
     {}
     (for [[n i] (map vector steps (range))]
       (timeline/timeline-step (cond-> {}
                                 (= i active-index)          (assoc :active? true)
                                 (< i active-index)        (assoc :visited? true)
                                 (= i (dec (count steps))) (assoc :last? true))
                               n)))))


;; TODO add plaid merchant
;; TODO each client only used once

(defn terms-override-row [terms-override-cursor]
  (com/data-grid-row
   (-> {:x-data   (hx/json {:show      (boolean (not (fc/field-value (:new? terms-override-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)}))
    (fc/with-field :vendor-terms-override/client
      (com/data-grid-cell
       {}
       (com/validated-field
        {:errors (fc/field-errors)}
        (com/typeahead {:name        (fc/field-name)
                        :error?      (fc/error?)
                        :autofocuse true
                        :class       "w-full grow shrink"
                        :placeholder "Search..."
                        :url         (bidi/path-for ssr-routes/only-routes :company-search)
                        :value       (fc/field-value)
                        :content-fn  (fn [c] (pull-attr (dc/db conn) :client/name c))}))))
    (fc/with-field :vendor-terms-override/terms
      (com/data-grid-cell
       {}
       (com/validated-field
        {:errors (fc/field-errors)}
        [:div.flex.items-baseline.gap-x-4
         (com/int-input {:name      (fc/field-name)
                         :value     (fc/field-value)
                         :class     "w-16"})
         "days"])))
    (com/data-grid-cell {:class "align-top"}
                        (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))


(defn automatically-paid-when-due-row [terms-override-cursor]
  (com/data-grid-row
   (-> {:x-data   (hx/json {:show      (boolean (not (fc/field-value (:new? terms-override-cursor))))})
        :data-key "show"
        :x-ref    "p"}
       hx/alpine-mount-then-appear)
   (list
    (com/data-grid-cell
     {}
     (com/validated-field {:errors (fc/field-errors (:db/id fc/*current*))}
                          (com/typeahead {:name     (fc/field-name (:db/id fc/*current*))
                                          :class    "w-full"
                                          :url      (bidi/path-for ssr-routes/only-routes
                                                                   :company-search)
                                          :value    (fc/field-value)
                                          :value-fn :db/id


                                          :content-fn #(pull-attr (dc/db conn) :client/name (:db/id %))
                                          :size       :small})))


    (com/data-grid-cell {:class "align-top"}
                        (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))


(defn- account-typeahead*
  [{:keys [name value client-id x-model]}]
  [:div.flex.flex-col
   (com/typeahead {:name        name
                   :placeholder "Search..."
                   :url         (str (bidi/path-for ssr-routes/only-routes :account-search) "?client-id=" client-id)
                   :id          name
                   :x-model     x-model
                   :value       value
                   :content-fn  (fn [value]
                                  (:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
                                                                       client-id)))})])

(defn account-override-row [terms-override-cursor]
  (alog/peek @terms-override-cursor)
  (let [client-id (fc/field-value (:vendor-account-override/client terms-override-cursor))]
    (com/data-grid-row
     (-> {:x-data   (hx/json {:show     (boolean (not (fc/field-value (:new? terms-override-cursor))))
                              :clientId client-id
                              :accountId (fc/field-value (:vendor-account-override/account terms-override-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)}))
      (fc/with-field :vendor-account-override/client
        (com/data-grid-cell
         {}
         (com/validated-field
          {:errors (fc/field-errors)}
          (com/typeahead {:name        (fc/field-name)
                          :error?      (fc/error?)
                          :x-model     "clientId"
                          :autofocuse  true
                          :class       "w-full grow shrink"
                          :placeholder "Search..."
                          :url         (bidi/path-for ssr-routes/only-routes :company-search)
                          :value       (fc/field-value)
                          :content-fn  (fn [c] (pull-attr (dc/db conn) :client/name c))}))))
      (fc/with-field :vendor-account-override/account
        (com/data-grid-cell
         {}
         (com/validated-field
          {:errors (fc/field-errors)}
          [:div {:hx-trigger "changed"
                 :hx-target  "next div"
                 :hx-vals    (format "js:{name: '%s', 'client-id': event.detail.clientId, value: event.detail.accountId || ''}" (fc/field-name))
                 :hx-get     (str (bidi/path-for ssr-routes/only-routes ::route/account-typeahead))
                 :x-init     "$watch('clientId', cid => $dispatch('changed', $data));"}]
          (account-typeahead* {:value     (fc/field-value)
                               :client-id client-id
                               :name      (fc/field-name)
                               :x-model   "accountId"}))))
      (com/data-grid-cell {:class "align-top"}
                          (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))))







(defn dialog* [{:keys [entity form-params form-errors] :as params}]
  (alog/peek ::dialog-entity form-params)
  (fc/start-form form-params form-errors
                 [:div {:x-data (hx/json {"vendorName"  (:vendor/name form-params)
                                          "showPrintAs" (boolean (not-empty (:vendor/print-as form-params)))
                                          "printAs"     (:vendor/print-as form-params)})
                        :class  "w-full h-full"}
                  [:form#my-form (-> {:hx-ext        "response-targets"
                                      :hx-swap       "outerHTML"
                                      :hx-target-400 "#form-errors .error-content"
                                      :hx-trigger "submit"
                                      :class         "h-full w-full"}
                                     (assoc (if (:db/id entity)
                                              :hx-put
                                              :hx-post)
                                            (str (bidi/path-for ssr-routes/only-routes ::route/save))))
                   (com/modal
                    {}

                    #_(terms-modal params)
                    #_(account-modal params)

                    [:div])]]))

(def form-schema (mc/schema
                  [:map
                   [:db/id {:optional true} [:maybe entity-id]]
                   [:vendor/name  [:string {:min 3 :decode/string strip}]]
                   [:vendor/print-as {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                   [:vendor/default-account entity-id]
                   [:vendor/terms {:optional true} [:maybe :int]]
                   [:vendor/automatically-paid-when-due {:optional true}
                    [:maybe
                     (many-entity {} [:db/id entity-id])]]
                   [:vendor/terms-overrides {:optional true}
                    (many-entity {}
                                 [:db/id [:or entity-id temp-id]]
                                 [:vendor-terms-override/terms :int]
                                 [:vendor-terms-override/client entity-id])]
                   [:vendor/account-overrides {:optional true}
                    (many-entity {}
                                 [:db/id [:or entity-id temp-id]]
                                 [:vendor-account-override/account entity-id]
                                 [:vendor-account-override/client entity-id])]
                   [:vendor/hidden {:default false}
                    [:boolean {:decode/string {:enter #(if (= % "on") true

                                                           (boolean %))}}]]
                   [:vendor/address {:optional true}
                    [:maybe
                     [:map
                      [:db/id {:optional true} [:or entity-id temp-id]]
                      [:address/street1 {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                      [: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}]]]]]]
                   [:vendor/legal-entity-tin {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                   [:vendor/legal-entity-name {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                   [:vendor/legal-entity-first-name {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                   [:vendor/legal-entity-middle-name {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                   [:vendor/legal-entity-last-name {:optional true}  [:maybe [:string {:min 3 :decode/string strip}]]]
                   [:vendor/legal-entity-tin-type {:optional true} [:maybe (ref->enum-schema "legal-entity-tin-type")]]
                   [:vendor/legal-entity-1099-type {:optional true} [:maybe (ref->enum-schema "legal-entity-1099-type")]]]))

(def merge-form-schema (mc/schema
                        [:map
                         [:source-vendor {:optional false} entity-id]
                         [:target-vendor {:optional false} entity-id]]))

(defn merge-dialog [{:keys [entity form-params form-errors]}]
  (modal-response
   (fc/start-form form-params form-errors
                  [:div {:class  "w-full h-full"}
                   [:form#my-form (-> {:hx-swap       "outerHTML"
                                       :hx-trigger    "submit"
                                       :class         "h-full w-full"
                                       :hx-put (str (bidi/path-for ssr-routes/only-routes ::route/merge-submit))})
                    (com/modal
                     {}
                     (com/modal-card
                      {}
                      [:div.m-2 "Merge Vendors"]

                      [:div.space-y-6.m-1
                       (fc/with-field :source-vendor
                         (com/validated-field {:label  "Source vendor (to be deleted)"
                                               :errors (fc/field-errors)}
                                              (com/typeahead {:name        (fc/field-name)
                                                              :placeholder "Search..."
                                                              :url         (bidi/path-for ssr-routes/only-routes
                                                                                          :vendor-search)
                                                              :id          (str "vendor-search")
                                                              :value       (fc/field-value)
                                                              :content-fn  (fn [c] (pull-attr (dc/db conn) :vendor/name c))})))
                       (fc/with-field :target-vendor
                         (com/validated-field {:label  "Target vendor"
                                               :errors (fc/field-errors)}
                                              (com/typeahead {:name        (fc/field-name)
                                                              :placeholder "Search..."
                                                              :url         (bidi/path-for ssr-routes/only-routes
                                                                                          :vendor-search)
                                                              :id          (str "vendor-search")
                                                              :value       (fc/field-value)
                                                              :content-fn  (fn [c] (pull-attr (dc/db conn) :vendor/name c))})))]
                      [:div.flex.justify-end
                       (com/form-errors {:errors (:errors fc/*form-errors*)})
                       [:div.flex.items-baseline.gap-x-4
                        (com/validated-save-button {:errors (seq form-errors)
                                                    :class  "w-48"}

                                                   "Merge")]]))]])))

(defn account-typeahead [{{:keys [name value client-id] :as qp} :query-params}]
  (html-response (account-typeahead* {:name      name
                                      :value     value
                                      :client-id client-id
                                      :x-model   "accountId"})))

(defrecord LegalEntityModal [linear-wizard]
  mm/ModalWizardStep
  (step-key [this]
    :legal)
  (edit-path [this request] [])
  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head [:div.flex [:div.p-2 "Legal Entity"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
                                                 [:span {:x-text "vendorName"}]]]
     :body (mm/default-step-body
            {}
            [:div {:class "w-[600px] h-[350px]"}
             [:div.grid.grid-cols-6.gap-x-4.gap-y-2
              [:div.col-span-6
               (fc/with-field :vendor/legal-entity-name
                 (com/validated-field {:label  "Legal Entity Name"
                                       :errors (fc/field-errors)}
                                      (com/text-input {:name        (fc/field-name)
                                                       :class       "w-full"
                                                       :autofocus true
                                                       :value       (fc/field-value)
                                                       :placeholder "Good Restaurant LLC"})))]
              [:div.col-span-6.text-center " - OR -"]
              [:div.col-span-2
               (fc/with-field :vendor/legal-entity-first-name
                 (com/validated-field {:label  "First Name"
                                       :errors (fc/field-errors)}
                                      (com/text-input {:name        (fc/field-name)
                                                       :value       (fc/field-value)
                                                       :placeholder "John"})))]
              [:div.col-span-2
               (fc/with-field :vendor/legal-entity-middle-name
                 (com/validated-field {:label  "Middle Name"
                                       :errors (fc/field-errors)}
                                      (com/text-input {:name        (fc/field-name)
                                                       :value       (fc/field-value)
                                                       :placeholder "C."})))]
              [:div.col-span-2
               (fc/with-field :vendor/legal-entity-last-name
                 (com/validated-field {:label  "Last Name"
                                       :errors (fc/field-errors)}
                                      (com/text-input {:name        (fc/field-name)
                                                       :value       (fc/field-value)
                                                       :placeholder "Riley"})))]
              [:div.col-span-2
               (fc/with-field :vendor/legal-entity-tin
                 (com/validated-field {:label  "TIN"
                                       :errors (fc/field-errors)}
                                      (com/text-input {:name        (fc/field-name)
                                                       :value       (fc/field-value)
                                                       :placeholder "John"})))]
              [:div.col-span-2
               (fc/with-field :vendor/legal-entity-tin-type
                 (com/validated-field {:label  "TIN Type"
                                       :errors (fc/field-errors)}
                                      (com/select {:name         (fc/field-name)
                                                   :allow-blank? true
                                                   :value        (some-> (fc/field-value) name)
                                                   :options      [["ein" "EIN"]
                                                                  ["ssn" "SSN"]]})))]
              [:div.col-span-2
               (fc/with-field :vendor/legal-entity-1099-type
                 (com/validated-field {:label  "1099 Type"
                                       :errors (fc/field-errors)}
                                      (com/select {:name         (fc/field-name)
                                                   :allow-blank? true
                                                   :value        (some-> (fc/field-value) name)
                                                   :options      (ref->select-options "legal-entity-1099-type")})))]]])
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate))
  (step-schema [this]
    (mut/select-keys form-schema #{:vendor/legal-entity-1099-type :vendor/legal-entity-first-name :vendor/legal-entity-last-name :vendor/legal-entity-tin
                                   :vendor/legal-entity-tin-type :vendor/legal-entity-middle-name :vendor/legal-entity-name}))
  (step-name [this]
    "Legal Entity"))

(defrecord AddressModal [linear-wizard]
  mm/ModalWizardStep
  (step-key [this]
    :address)
  (edit-path [this request] [])
  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head [:div.flex [:div.p-2 "Address"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
                                            [:span {:x-text "vendorName"}]]]
     :body (mm/default-step-body
            {}
            [:div.space-y-1 {:class "w-[600px] h-[350px]"}
             (fc/with-field-default :vendor/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)})))]])])
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate))
  (step-schema [this]
    (mut/select-keys form-schema #{:vendor/address}))
  (step-name [this]
    "Address"))

(defrecord AccountModal [linear-wizard]
  mm/ModalWizardStep
  (step-key [this]
    :account)
  (edit-path [this request]
    [])
  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head [:div.flex [:div.p-2 "Account Assignments"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
                                                        [:span {:x-text "vendorName"}]]]
     :body (mm/default-step-body
            {}
            [:div.space-y-1 {:class "w-[600px] h-[350px] "}
             (fc/with-field :vendor/default-account
               (alog/info ::acount-check :a (fc/field-value))
               (com/validated-field {:label  "Default Account"
                                     :errors (fc/field-errors)}
                                    (com/typeahead {:name        (fc/field-name)
                                                    :error?      (fc/error?)
                                                    :autofocus true
                                                    :class       "w-96"
                                                    :placeholder "Search..."
                                                    :url         (bidi/path-for ssr-routes/only-routes :account-search)
                                                    :value       (fc/field-value)
                                                    :content-fn  (fn [c] (pull-attr (dc/db conn) :account/name c))})))
             (fc/with-field :vendor/account-overrides
               (com/validated-field
                {:errors (fc/field-errors)
                 :label "Account Overrides"}
                (com/data-grid {:headers [(com/data-grid-header {} "Client")
                                          (com/data-grid-header {} "Account")
                                          (com/data-grid-header {:class "w-16"})]}
                               (fc/cursor-map #(account-override-row %))
                               (com/data-grid-new-row {:colspan   3
                                                       :hx-get    (bidi/path-for ssr-routes/only-routes ::route/new-account-override)
                                                       :index     (count (fc/field-value))}
                                                      "New override"))))])
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate))
  (step-schema [this]
    (mut/select-keys (mm/form-schema linear-wizard) #{:vendor/account-overrides :vendor/default-account}))
  (step-name [this]
    "Account Assignment"))

;; TODO signature
(defrecord TermsModal [linear-wizard]
  mm/ModalWizardStep
  (step-key [this]
    :terms)
  (edit-path [this request]
    [])
  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head [:div.flex [:div.p-2 "Terms"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
                                          [:span {:x-text "vendorName"}]]]
     :body (mm/default-step-body
            {}
            [:div.space-y-1  {:class "w-[600px] h-[350px]"}
             (fc/with-field :vendor/terms
               (com/validated-field {:label  "Terms"
                                     :errors (fc/field-errors)}
                                    [:div.flex.items-baseline.gap-x-4
                                     (com/int-input {:name      (fc/field-name)
                                                     :autofocus true
                                                     :value     (fc/field-value)})
                                     "days"]))
             (fc/with-field :vendor/terms-overrides
               (com/validated-field
                {:errors (fc/field-errors)
                 :label "Terms Overrides"}
                (com/data-grid {:headers [(com/data-grid-header {} "Client")
                                          (com/data-grid-header {:class "w-16"} "Terms")
                                          (com/data-grid-header {:class "w-16"})]}
                               (fc/cursor-map #(terms-override-row %))
                               (com/data-grid-new-row {:colspan   3
                                                       :hx-get    (bidi/path-for ssr-routes/only-routes
                                                                                 ::route/new-terms-override)
                                                       :index     (count (fc/field-value))}
                                                      "New override"))))

             (fc/with-field :vendor/automatically-paid-when-due
               (com/validated-field
                {:errors (fc/field-errors)
                 :label "Automatically pay when due"}
                (com/data-grid {:headers [(com/data-grid-header {} "Client")
                                          (com/data-grid-header {:class "w-16"})]}
                               (fc/cursor-map #(automatically-paid-when-due-row %))
                               (com/data-grid-new-row {:colspan   2
                                                       :hx-get    (bidi/path-for ssr-routes/only-routes ::route/new-automatic-payment)
                                                       :index     (count (fc/field-value))}
                                                      "New automatic payment for client"))))])
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate))
  (step-schema [this]
    (mut/select-keys (mm/form-schema linear-wizard) #{:vendor/terms :vendor/terms-overrides :vendor/automatically-paid-when-due}))
  (step-name [this]
    "Terms"))

(defrecord InfoModal [linear-wizard]
  mm/ModalWizardStep
  (step-name [this]
    "Basic Info")

  (step-key [this]
    :info)

  (edit-path [this request] [])

  (step-schema [this]
    (mut/select-keys (mm/form-schema linear-wizard) #{:vendor/name :vendor/print-as :vendor/hidden}))

  (render-step [this _]
    (mm/default-render-step
     linear-wizard this
     :head [:div.flex [:div.p-2 "Basic Info"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
                                               [:span {:x-text "vendorName"}]]]
     :body (mm/default-step-body {}
                                 [:div.space-y-1.mt-4 {:class "w-[600px] h-[350px]"}
                                  (fc/with-field :vendor/name
                                    (com/validated-field {:label  "Name"
                                                          :errors (fc/field-errors)}
                                                         (com/text-input {:name      (fc/field-name)
                                                                          :value     (fc/field-value)
                                                                          :x-model   "vendorName"
                                                                          :autofocus true
                                                                          :class     "w-96"})))
                                  (com/validated-field
                                   {}
                                   [:div (com/checkbox
                                          {:x-model "showPrintAs" "@change" "if (!showPrintAs) { printAs = ''; } "}
                                          "Use different name for checks")])

                                  (fc/with-field :vendor/print-as
                                    (com/validated-field (-> {:label  "Print as"
                                                              :errors (fc/field-errors)
                                                              :x-show "showPrintAs"}
                                                             hx/alpine-appear
                                                             hx/alpine-disappear)
                                                         (com/text-input {:name    (fc/field-name)
                                                                          :x-model "printAs"
                                                                          :value   (fc/field-value)
                                                                          :class   "w-96"})))

                                  (fc/with-field :vendor/hidden
                                    (alog/peek (cursor/path fc/*current*))
                                    (com/checkbox {:name    (fc/field-name)
                                                   :value   (boolean (fc/field-value)) ;
                                                   :checked (alog/peek :checked (fc/field-value))}
                                                  "Admin-only"))])
     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
     :validation-route ::route/navigate)))

;; have a clear way to set up the form to handle the unexpected-errors
;; TODO feature flags
;; TODO move signature to client page
(defrecord VendorWizard [current-step]
  mm/LinearModalWizard
  (hydrate-from-request [this _]
    this)
  (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 {"vendorName"  (:vendor/name (:snapshot multi-form-state))
                                                           "showPrintAs" (boolean (not-empty (:vendor/print-as (:snapshot multi-form-state))))
                                                           "printAs"     (:vendor/print-as (:snapshot multi-form-state))})))))
  (steps [_]
    [:info
     :terms
     :account
     :address
     :legal])

  (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)
              :terms (->TermsModal this)
              :account (->AccountModal this)
              :address (->AddressModal this)
              :legal (->LegalEntityModal this)}
             step-key))))
  (form-schema [this] form-schema)
  (submit [this {:keys [multi-form-state request-method identity entity] :as request}]
    (let [snapshot (mc/decode
                    form-schema
                    (:snapshot multi-form-state)
                    mt/strip-extra-keys-transformer)
          entity (cond-> snapshot
                   (= :post request-method) (assoc :db/id "new"))
          {:keys [tempids]} (audit-transact [[:upsert-entity entity]]
                                            (:identity request))
          updated-vendor    (dc/pull (dc/db conn)
                                     default-read
                                     (or (get tempids (:db/id entity)) (:db/id entity)))]
      (solr/index-documents-raw
       solr/impl
       "vendors"
       [{"id"     (:db/id updated-vendor)
         "name"   (:vendor/name updated-vendor)
         "hidden" (boolean (:vendor/hidden updated-vendor))}])
      (html-response
       (row* identity updated-vendor {: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-vendor))
                                                  "hx-reswap" "outerHTML"))))))

(def vendor-wizard (->VendorWizard :info))


(def key->handler
  (apply-middleware-to-all-handlers
   (->>
    {::route/page                  (helper/page-route grid-page)
     ::route/table                 (helper/table-route grid-page)
     ::route/new                   (-> mm/open-wizard-handler
                                       (mm/wrap-init-multi-form-state (fn [_]
                                                                        (mm/->MultiStepFormState {}
                                                                                                 []
                                                                                                 {})))
                                       (mm/wrap-wizard vendor-wizard))
     ::route/merge                 merge-dialog
     ::route/merge-submit          (-> merge-submit
                                       (wrap-schema-enforce :form-schema merge-form-schema)
                                       (wrap-form-4xx-2 merge-dialog))

     ::route/save (-> mm/submit-handler
                      (mm/wrap-wizard vendor-wizard)
                      (mm/wrap-decode-multi-form-state)
                      (wrap-entity [:form-params :db/id] default-read))
     ::route/navigate
     (-> mm/next-handler
         (mm/wrap-wizard vendor-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/edit (-> mm/open-wizard-handler
                      (mm/wrap-wizard vendor-wizard)
                      (mm/wrap-init-multi-form-state (fn [request]
                                                       (mm/->MultiStepFormState (:entity request)
                                                                                []
                                                                                (:entity request))))
                      (wrap-entity [:route-params :db/id] default-read)
                      (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
     ::route/new-terms-override  (add-new-entity-handler [:step-params :vendor/terms-overrides]
                                                         (fn [cursor _] (terms-override-row cursor)))

     ::route/account-typeahead (-> account-typeahead
                                   (wrap-schema-enforce :query-schema [:map
                                                                       [:name :string]
                                                                       [:client-id {:optional true}
                                                                        [:maybe entity-id]]
                                                                       [:value {:optional true}
                                                                        [:maybe entity-id]]]))
     ::route/new-automatic-payment (add-new-primitive-handler [:step-params :vendor/automatically-paid-when-due]
                                                              {}
                                                              automatically-paid-when-due-row)

     ::route/new-account-override  (add-new-entity-handler [:step-params :vendor/account-overrides]
                                                           (fn [cursor _] (account-override-row cursor)))})
   (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)))))

