(ns auto-ap.ssr.admin
  (:require
   [auto-ap.datomic :refer [conn]]
   [auto-ap.logging :as alog]
   [auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
   [auto-ap.ssr.ui :refer [base-page]]
   [auto-ap.ssr.utils :refer [html-response]]
   [auto-ap.time :as atime]
   [clj-time.coerce :as coerce]
   [clojure.string :as str]
   [datomic.client.api :as dc]
   [hiccup2.core :as hiccup]))

(defn tx-rows->changes [history]
  (->> history 
       (group-by (fn [[a _ t]]
                   [a t]))
       (map (fn [[[a t] changes]]
              (let [changes (-> (reduce
                                 (fn [acc [_ v _ added]]
                                   (if added
                                     (assoc acc :added v)
                                     (assoc acc :removed v)))
                                 {}
                                 changes))]
                [t a changes])))))

(def error-script
  (hiccup/raw "on htmx:responseError from me set event.detail.target's innerHTML to event.detail.xhr.responseText end"))

(defn format-value [v]
  (cond (inst? v)
        (-> v
            coerce/to-date-time
            atime/localize
            (atime/unparse atime/normal-date))

        (nil? v)
        [:em "(none)"]

        (and (integer? v)
             (> v 1000000))
        [:span 
         [:a {:hx-get (str "/admin/history/" v)
              :hx-swap "innerHTML"
              :hx-push-url "true"
              :hx-target "#history-table"}
          v]
         " [" [:a
               {:hx-get     (str "/admin/history/inspect/" v)
                :hx-swap    "innerHTML"
                :hx-target  "#inspector"
                :hx-trigger "click"
                "_" error-script}
               "snapshot"] "]"]


        :else
        (pr-str v)))

(defn page-template [& {:keys [table entity-id]}]
  [:div 
   [:div.columns 
    [:div.column.is-4 
     [:form {"hx-target" "#history-table"
             "hx-post"   "/admin/history/search"
             "hx-swap"   "innerHTML"
             "_"         (hiccup/raw "on htmx:beforeRequest toggle @disabled on me then toggle .is-loading on <#dig/> end
                   on htmx:afterRequest toggle @disabled on me then toggle .is-loading on <#dig /> end")
             }
      [:div.field.is-grouped 
       [:p.control {}
        [:input.input {:type "text" :name "entity-id" :placeholder "Entity id" :value entity-id}]]
       [:p.control
        [:button#dig.button.is-primary {}
         "Dig"]]]]]]
   [:div#history-table
    table]])

(defn table [entity-id best-guess-entity history]
  [:div [:h1.title "History for "
         (str/capitalize best-guess-entity)
         " "
         entity-id]
   [:div.columns
    [:div.column.is-9
     [:table.table.compact.grid {:style "width: 100%"}
      [:thead
       [:tr
        [:td {:style "width: 14em"} "Date"]
        [:td {:style "width: 14em"} "User"]
        [:td {:style "width: 18em"} "Field"]
        [:td "From"]
        [:td "To"]]]
      [:tbody
       (for [[tx a c] history]
         [:tr 
          [:td [:div [:div (some-> (:db/txInstant tx)
                                   coerce/to-date-time
                                   atime/localize
                                   (atime/unparse atime/standard-time))
                      ]
                [:div.tag (:db/id tx)]]]
          [:td (str (:audit/user tx))]
          [:td (namespace a) ": " (name a)]

          [:td
           [:div.tag.is-danger.is-light
            [:span 
             (format-value (:removed c))]]]
          [:td
           [:div.tag.is-primary.is-light
            [:span 
             (format-value (:added c))]]]])]
      ]]
    [:div.column.is-3
     [:div#inspector]]]])

(defn history-search [{:keys [form-params params] :as request}]
  (try
    (let [entity-id         (Long/parseLong (or (some-> (:entity-id form-params) not-empty)
                                        (:entity-id params)
                                        (get params "entity-id")
                                        (get form-params "entity-id")))
          history           (->> 
                              (dc/q '[:find ?a2 ?v (pull ?tx [:db/txInstant :audit/user :db/id]) ?ad 
                                     :in $ $$ ?i
                                     :where
                                     [$$ ?i ?a ?v ?tx ?ad]
                                     [$ ?a :db/ident ?a2]]
                                   (dc/db conn)
                                   (dc/history (dc/db conn))
                                   entity-id )
                              tx-rows->changes
                              (sort-by (comp :db/id first))
                              vec)
          best-guess-entity (or (->> history
                                     (group-by  
                                       (comp 
                                         namespace
                                         second)
                                       )
                                     (map (fn [[k v]]
                                            [k v]))
                                     (sort-by second)
                                     last
                                     first)
                                "?")]

      (if (get (:headers request) "hx-request")
        (html-response 
          (table entity-id best-guess-entity history))
        (base-page request (page-template :table (table entity-id best-guess-entity history)
                                  :entity-id entity-id)
                   (admin-side-bar :admin-history))))
    (catch NumberFormatException _
      (html-response 
        [:div.notification.is-danger.is-light
         "Cannot parse the entity-id " (or (:entity-id form-params)
                                           (:entity-id params))

         ". It should be a number."]))))

(defn inspect [{{:keys [entity-id]} :params  :as request}]
  (alog/info ::inspect
             :request request)
  (try
    (let [entity-id (Long/parseLong entity-id)
          data (dc/pull (dc/db conn)
                       '[*]
                       entity-id)]

      (html-response 
        [:div.box {:style {:position       "sticky"
                                     :display        "inline-block"
                                     :vertical-align "top"
                                     :overflow-y     "auto"
                                     :max-height     "100vh"
                                     :top            "0px"
                                     :bottom         "0px"}}
         [:div {:style {:display "inline-block"}}
          [:h1.title "Snapshot of "
           entity-id]
          [:ul
           (for [[k v] data]
             [:li [:strong k] ":" (format-value v)]
             )]]]))
    (catch NumberFormatException _
      (html-response 
        [:div.notification.is-danger.is-light
         "Cannot parse the entity-id " entity-id ". It should be a number."]))))

(defn history [{:keys [matched-route] :as request}]
  (base-page request
             (page-template )
             (admin-side-bar matched-route)))




