(ns auto-ap.forms
  (:require [re-frame.core :as re-frame]
            [re-frame.interceptor :as i]
            [auto-ap.status :as status]
            [auto-ap.views.utils :refer [dispatch-event bind-field]]))


(re-frame/reg-sub
 ::form
 (fn [db [_ x]]
   (get (-> db ::forms) x)))


(re-frame/reg-sub 
 ::is-loading?
 (fn [db [_ x]]
   (if (#{"loading" :loading} (get-in db [::forms x :status]) )
     true
     false)))

(re-frame/reg-sub 
 ::loading-class
 (fn [db [_ x]]
   (if (#{"loading" :loading} (get-in db [::forms x :status]) )
     "is-loading"
     "")))

(defn start-form
  ([db form data]
   (start-form db form data nil))
  ([db form data complete-listener]
   (assoc-in db [::forms form] {:error nil
                                :active? true
                                :id (random-uuid)
                                :status nil
                                :data data
                                :complete-listener complete-listener})))

(defn ^:depracated saved-form [db form data]
  (update-in db [::forms form]
             assoc :error nil :status nil :data data))

(defn triggers-saved [form data-key]
  (i/->interceptor
   :id :triggers-saved
   :before (fn [context]
            context)
   :after (fn [context]
            (let [db (i/get-coeffect context :db)
                  result (get-in (i/get-coeffect context :event) [1 data-key])]
              
              (cond-> context 
                true
                (i/assoc-effect :db (update-in db
                                     [::forms form]
                                     assoc :error nil :status nil :data result))

                (get-in db [::forms form :complete-listener])
                (i/assoc-effect :dispatch (conj (get-in db [::forms form :complete-listener ])
                                                result)))))))
(defn stop-form [db form]
  (update db ::forms dissoc form))

(re-frame/reg-event-db
 ::form-closing
 (fn  [db [_ f]]
   (-> db
       (stop-form f))))

(defn in-form
  [form-name]
  (re-frame/path [::forms form-name]))

(re-frame/reg-event-db
 ::change
 (fn  [db [_ form & path-pairs]]
   
   (reduce
    (fn [db [path value]]
      (assoc-in db (into [::forms form :data] path) value))
    db
    (partition 2 path-pairs))))

(defn change-handler [form customize-fn]
  (fn  [db [_ & path-pairs]]
    
    (reduce
     (fn [db [path value]]
       (let [updated (assoc-in db (into [::forms form :data] path) value)]
         (reduce 
          (fn [updated [path value ]]
            (assoc-in updated (into [::forms form :data] path) value))
          updated
          (partition 2 (customize-fn (get-in updated [::forms form :data]) path value)))))
     db
     (partition 2 path-pairs))))

(re-frame/reg-event-db
 ::save-error
 (fn  [db [_ form result]]
   (println result)
   
   (-> db
       (assoc-in [::forms form :status] :error)
       (assoc-in [::forms form :error] (or (:message (first result))
                                           result)))))


(defn ^:deprecated side-bar-form [{:keys [form]} children]
  [:div [:a.delete.is-pulled-right {:on-click (dispatch-event [::form-closing form])}] [:div children]])


;; TODO ^:deprecated 
(defn loading [db id]
  (-> db 
      (assoc-in [::forms id :status] :loading)
      (assoc-in [::forms id :error] nil)))

(defn settles [{:keys [event time key]}]
  (i/->interceptor
   :id :settles
   :before (fn [context]
            context)
   :after (fn [context]
            (i/assoc-effect context :dispatch-debounce {:event event
                                                        :time time
                                                        :key key}))))

(defn triggers-loading [form]
  (re-frame/enrich
   (fn [db event]
     (loading db form))))
(defn triggers-stop [form]
  (re-frame/enrich
   (fn [db event]
     (stop-form db form))))

(defn triggers-stop-loading [form]
  (re-frame/enrich
   (fn [db event]
     (assoc-in db [::forms form :status] nil))))

(defn save-succeeded [db id]
  (-> db 
      (assoc-in [::forms id :status] nil)
      (assoc-in [::forms id :error] nil)))


(defn vertical-form [{:keys [can-submit id change-event submit-event ]}]
  {:form 
   (fn [{:keys [title] :as params} & children]
     (let [{:keys [data active? error]} @(re-frame/subscribe [::form id])
           can-submit @(re-frame/subscribe can-submit)]
       
       [:form { :on-submit (fn [e]
                             (when (.-stopPropagation e)
                               (.stopPropagation e)
                               (.preventDefault e))
                             (when can-submit
                               (re-frame/dispatch-sync (vec (conj submit-event params)))))} 
        [:h1.title.is-2 title]
        [:<>
         children]]))

   :form-inline
   (fn [{:keys [title] :as params} children]
     (let [{:keys [data active? error]} @(re-frame/subscribe [::form id])
           can-submit @(re-frame/subscribe can-submit)]
       
       [:form { :on-submit (fn [e]
                             (when (.-stopPropagation e)
                               (.stopPropagation e)
                               (.preventDefault e))
                             (when can-submit
                               (re-frame/dispatch-sync (vec (conj submit-event params)))))} 
        (when title
          [:h1.title.is-2 title])
        children]))
   :raw-field (fn [control]
                (let [{:keys [data]} @(re-frame/subscribe [::form id])]
                  [bind-field (-> control
                                  (assoc-in [1 :subscription] data)
                                  (assoc-in [1 :event] change-event))]))
   :field-holder (fn [label control]
                   [:div.field
                    (when label [:p.help label])
                    [:div.control control]])
   :field ^{:key "field"}
   (fn [label control]
     (let [{:keys [data]} @(re-frame/subscribe [::form id])]
       [:div.field
        (when label [:p.help label])
        [:div.control [bind-field (-> control
                                      (assoc-in [1 :subscription] data)
                                      (assoc-in [1 :event] change-event))]]]))

   :error-notification
   (fn []
     (when-let [error (:error @(re-frame/subscribe [::form id]))]
       ^{:key error}
       [:div.has-text-danger.animated.fadeInUp {} error]))
   :submit-button (fn [child]
                    (let [error (:error @(re-frame/subscribe [::form id]))
                          status @(re-frame/subscribe [::status/single id])
                          can-submit @(re-frame/subscribe can-submit)]
                      [:button.button.is-medium.is-primary.is-fullwidth {:disabled (or (status/disabled-for status)
                                                                                        (not can-submit))
                                                                         :class (status/class-for status) }
                       child]))})

