(ns auto-ap.forms
  (:require [re-frame.core :as re-frame]
            [re-frame.interceptor :as i]
            [auto-ap.status :as status]
            [malli.core :as m]))


(re-frame/reg-sub
 ::form
 (fn [db [_ x]]
   (update (get (-> db ::forms) x)
           :visited (fn [v]
                      (or v #{})))))

(re-frame/reg-sub
  ::field
  (fn [db [_ x f]]
    (-> (get (-> db ::forms) x)
        :data
        (get-in f))))

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

(re-frame/reg-event-db
  ::start-form
  (fn [db [_ id data]]
    (start-form db id 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))))

(re-frame/reg-event-db
  ::reset
  (fn  [db [_ form v]]
    (assoc-in db [::forms form :data] v)))

(re-frame/reg-event-db
  ::visited
  (fn  [db [_ form & paths]]
    (update-in db [::forms form :visited] (fn [v]
                                            (set (into v paths))))))

(re-frame/reg-event-db
  ::check-problems
  (fn  [db [_ form schema]]
    (assoc-in db [::forms form :problems]
              (when schema (m/explain schema (get-in db [::forms form :data]))))))

(re-frame/reg-event-db
  ::attempted-submit
  (fn  [db [_ form ]]
    (assoc-in db [::forms form :attempted-submit?] true)))

(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)))))

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

#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(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 _]
     (loading db form))))

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

(defn triggers-stop-loading [form]
  (re-frame/enrich
   (fn [db _]
     (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)))


