(ns auto-ap.ssr.components.data-grid
  (:require
   [auto-ap.ssr-routes :as ssr-routes]
   [auto-ap.ssr.components.buttons :refer [a-button-]]
   [auto-ap.ssr.components.card :refer [content-card-]]
   [auto-ap.ssr.components.inputs :as inputs]
   [auto-ap.ssr.components.paginator :refer [paginator-]]
   [auto-ap.ssr.hiccup-helper :as hh]
   [auto-ap.ssr.hx :as hx :refer [js-fn]]
   [bidi.bidi :as bidi]
   [hiccup.util :as hu]
   [hiccup2.core :as hiccup]))

(defn header- [params & rest]
  (into [:th.px-4.py-3 {:scope "col" :class (:class params)
                        "@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params))
                        :style (:style params)}]
        (if (:sort-key params)
          [(into [:a {:href "#"} ] rest)]
          rest)))

(defn sort-header- [params & rest]
  [:th.px-4.py-3 {:scope "col" :class (:class params)
                        "@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params)) }
   (into [:a {:href "#"} ] rest)])

(defn row- [params & rest]
  (into [:tr (update params
                     :class str " border-b dark:border-gray-600 group hover:bg-gray-100 dark:hover:bg-gray-700")] rest))

(defn cell- [params & rest]
  (into [:td.px-4.py-2 (update params
                               :class #(str (-> ""
                                               (hh/add-class (or % ""))))) ] 
        rest))

(defn right-stack-cell- [params & rest]
  (cell- params (into [:div.flex.flex-row-reverse.items-center.justify-between 
                       rest])))

(defn checkbox-header- [params & rest]
  [:th {:scope "col", :class "p-4"}
   [:div {:class "flex items-center"}
    [:input (merge {:id "checkbox-all", :type "checkbox", :class inputs/default-checkbox-classes :name (:name params) :value (:value params)} params)]
    [:label {:for "checkbox-all", :class "sr-only"} "checkbox"]]])

(defn data-grid-
  [{:keys [headers thead-params id] :as params} & rest]
  [:div.shrink.overflow-y-scroll 
   [:table (merge {:class "w-full text-sm text-left text-gray-500 dark:text-gray-400 shrink"}
                  (dissoc params :headers :thead-params))
    [:thead (update thead-params :class #(-> "text-xs text-gray-800 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 group-[.raw]:sticky group-[.raw]:z-10 group-[.raw]:top-0"
                                             (hh/add-class (or % ""))))
     (into 
       [:tr]
       headers)]
    (into 
      [:tbody {}]
      rest)]])

;; needed for tailwind
;; lg:table-cell  md:table-cell

(defn raw-table-card [params & children]
  [:div params children])

(defn data-grid-card- [{:keys [id
                               route
                               title
                               paginate?
                               action-buttons
                               total
                               subtitle
                               thead-params
                               start
                               per-page
                               root-params
                               flash-id
                               headers
                               raw?
                               rows] :as params} & children]
  (let [card (if raw? raw-table-card content-card-)]
    (card
       (cond-> { :id id :class (cond-> "group" raw? (hh/add-class "raw h-full flex flex-col overflow-hidden")) }
         root-params (merge root-params)
         route (assoc 
                 :hx-get     (bidi/path-for ssr-routes/only-routes
                                            route
                                            :request-method :get)
                 :hx-trigger "clientSelected from:body, invalidated from:body"
                 :hx-swap    "outerHTML swap:300ms"))
       [:div {:class "  flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-baseline lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"}
        [:h1.text-2xl.mb-3.font-bold title]
        [:div {:class "flex items-center flex-1 space-x-4"}
         [:h5
          (when subtitle
            [:span subtitle])]]
        (into [:div {:class "group-[.raw]:hidden flex flex-col flex-shrink-0 space-y-3 md:flex-row md:items-center lg:justify-end md:space-y-0 md:space-x-3"}]
              action-buttons)]
       [:div {:class "overflow-x-auto contents"}
        (data-grid- {:headers      headers
                     :thead-params thead-params}
                    rows)]

       (when (or paginate?
                 (nil? paginate?))
         [:div {:class "contents group-[.raw]:block"}
          (paginator- {:start           start
                       :end             (Math/min (+ start per-page) total)
                       :per-page        per-page
                       :total           total
                       :a-params        (fn [page]
                                          ;; TODO it might be good to have a more global form defined in the specific page
                                          ;; with elements that are part of item
                                          ;; that way this is not deeply coupled
                                          ;; for example, including filters and pagination is awkward
                                          ;; TODO the other way to think about this is that we want the request to include
                                          ;; all of the correct parameters, not parameters to merge with the current ones.
                                          ;; think sorting, filters, pagination
                                          {:hx-get       (hu/url (bidi/path-for ssr-routes/only-routes
                                                                                route
                                                                                :request-method :get)
                                                                 {:start (* page per-page)})
                                           :hx-target    (str "#" id)
                                           :hx-swap      "outerHTML show:#app:top"
                                           :hx-indicator (str "#" id)})
                       :per-page-params {:hx-get       (hu/url (bidi/path-for ssr-routes/only-routes
                                                                              route
                                                                              :request-method :get))
                                         :hx-trigger   "change"
                                         :hx-include   "this"
                                         :hx-target    (str "#" id) ;
                                         :hx-swap      "outerHTML show:#app:top"
                                         :hx-indicator (str "#" id)}})])
       children
       [:div {:class "htmx-indicator absolute -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2 overflow-hidden w-full h-full"}
        [:div {:class "flex items-center justify-center w-full h-full border border-gray-200 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700 bg-opacity-50" }
         [:div {:class "px-3 py-1 text-xs font-medium leading-none text-center text-blue-800 bg-blue-200 rounded-full animate-pulse dark:bg-blue-900 dark:text-blue-200"} "loading..."]]])))

(defn new-row- [{:keys [index colspan tr-params row-offset] :as params} & content]
  (row-
    (merge {:class  "new-row"
"x-on:htmx:after-settle.camel" "let options=$el.parentNode.querySelectorAll('tr'); let target=options[options.length-2]; $nextTick(() => $focus.within(target).first())"

            :x-data (hx/json {:newRowIndex index
                              :offset (or row-offset 0)}) }
           tr-params)
    (cell- {:colspan colspan
            :class   "bg-gray-100"}
           [:div.flex.justify-center
            (a-button- (merge
                        (dissoc params :index :colspan)
                        {"@click.prevent"    "$dispatch('newRow', {index: (newRowIndex++)})"
                         :color      :secondary
                         :hx-trigger "newRow"
                         :hx-vals    (hiccup/raw "js:{index: event.detail.index }")
                         :hx-target  "closest .new-row"
                         :hx-swap    "beforebegin"
                         })
                       content)])))
