(ns auto-ap.views.pages.admin.clients
  (:require-macros [cljs.core.async.macros :refer [go]]
                   [clojure.string :as str])
  (:require [re-frame.core :as re-frame]
            
            [reagent.core :as reagent]
            [clojure.spec.alpha :as s]
            [clojure.string :as str]
            [auto-ap.subs :as subs]
            [auto-ap.events :as events]
            [auto-ap.entities.clients :as entity]
            [auto-ap.views.components.address :refer [address-field]]
            [auto-ap.views.components.layouts :refer [side-bar-layout]]
            [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
            [auto-ap.views.utils :refer [login-url dispatch-event dispatch-value-change bind-field horizontal-field]]
            [auto-ap.views.components.modal :refer [action-modal]]
            [cljs.reader :as edn]
            [auto-ap.routes :as routes]
            [bidi.bidi :as bidi]))

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

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

(defn start-form [db form data]
  (assoc-in db [::forms form] {:error nil
                               :status nil
                               :data data}))
(defn stop-form [db form]
  (update db ::forms dissoc form))

(re-frame/reg-event-db
 ::new
 (fn  [db [_ client-id]]
   (-> db
       (assoc-in [:admin :adding-client?] true)
       (start-form ::new-client (assoc {} :new-account {:type :check})))))



(re-frame/reg-event-fx
 ::edit-client-clicked
 (fn  [{:keys [db]} [_ client-id]]
   {:db (-> db
            (start-form ::new-client (assoc (get (:clients db) client-id) :new-account {:type :check}))
            (assoc-in [:admin :adding-client? ] true))}))



(re-frame/reg-event-fx
 ::save-new-client
 [(re-frame/path [::forms ::new-client])]
 (fn  [{{new-client-data :data :as new-client-form} :db} _]

   (let [new-client-req {:id (:id new-client-data),
                         :name (:name new-client-data)
                         :code (:code new-client-data) ;; TODO add validation can't change
                         :email (:email new-client-data)
                         :locations (:locations new-client-data)
                         :address {:street1 (:street1 (:address new-client-data))
                                   :street2 (:street2 (:address new-client-data)),
                                   :city (:city (:address new-client-data))
                                   :state (:state (:address new-client-data))
                                   :zip (:zip (:address new-client-data))}
                         :bank-accounts (map (fn [{:keys [number name check-number type id code bank-name routing bank-code]}]
                                               {:number number
                                                :name name
                                                :check-number check-number
                                                :type type
                                                :id id
                                                :code (str (:code new-client-data) "-" code)
                                                :bank-name bank-name
                                                :routing routing
                                                :bank-code bank-code})
                                             (:bank-accounts new-client-data))}
         user @(re-frame/subscribe [::subs/token])]
     
     (if (s/valid? ::entity/client new-client-req)
       
       {:db (-> new-client-form
                (assoc :status :loading)
                (assoc :error nil))
        :graphql
        {:token user
         :query-obj {:venia/operation {:operation/type :mutation
                                       :operation/name "EditClient"}
                     :venia/queries [{:query/data [:edit-client
                                                   {:edit-client new-client-req}
                                                   [:id :name :code :email :locations [:address [:street1 :street2 :city :state :zip]] [:bank-accounts [:id :number :check-number :name :code :bank-code :bank-name :routing]]]]}]}  
         :on-success [::save-complete]
         :on-error [::save-error]}}
       {:db new-client-form}))))

(re-frame/reg-event-db
 ::save-complete
 (fn  [db [_ client]]
   (-> db
       (stop-form ::new-client)
       (assoc-in [:admin :adding-client?] false)
       (assoc-in [:clients (:id (:edit-client client))] (:edit-client client)))))

(re-frame/reg-event-db
 ::save-error
 [(re-frame/path [::forms ::new-client])]
 (fn  [db [_ result]]
   (-> db
       (assoc :status :error)
       (assoc :error (:message (first result))))))

(re-frame/reg-event-db
 ::change-new
 [(re-frame/path [::forms ::new-client :data])]
 (fn  [db [_ path value]]
   (assoc-in db path value)))

(re-frame/reg-event-db
 ::add-new-location
 [(re-frame/path [::forms ::new-client :data])]
 (fn [client _]
   (-> client
       (update :locations conj (:location client))
       (dissoc :location))))


(re-frame/reg-event-db
 ::add-new-bank-account
 [(re-frame/path [::forms ::new-client :data])]
 (fn [{:keys [new-account] :as client} _]
   (let [new-account (-> new-account
                         (update :check-number #(if (seq %) (js/parseInt %) nil))
                         (update :yodlee-account-id #(if (seq %) (js/parseInt %) nil)))]
     (if ((->> client (:bank-accounts)
               (map :code)
               set) (:code new-account))
       client
       (-> client
           (update :bank-accounts conj new-account )
           (assoc :new-account {:type :check}))))))

(re-frame/reg-event-db
 ::remove-new-bank-account
 [(re-frame/path [::forms ::new-client :data])]
 (fn [db [_ code]]
   (update db :bank-accounts
              (fn [bas]
                (filter #(not= (:code %) code) bas)))))

(defn clients-table []
  (let [clients (re-frame/subscribe [::subs/clients])
        editing-client (:client  @(re-frame/subscribe [::subs/admin]))]
    [:table {:class "table", :style {:width "100%"}}
     [:thead  
      [:tr  
       [:th {:style {:width "50%"}} "Name"]
       [:th {:style {:width "10%"}} "Name"]
       [:th {:style {:width "20%"}} "Locations"]
       [:th {:style {:width "20%"}} "Email"]]]
     [:tbody (for [{:keys [id name email code locations] :as c} @clients]
               ^{:key (str name "-" id )}
               [:tr {:on-click (fn [] (re-frame/dispatch [::edit-client-clicked id]))
                     :style {"cursor" "pointer"}}
                [:td name]
                [:td code]
                [:td (str/join ", " locations)]
                [:td email]])]]))



(defn admin-clients-content []
  [:div
   (let [clients (re-frame/subscribe [::subs/clients])
         editing-client (:client @(re-frame/subscribe [::subs/admin]))]
     [:div
      [:h1.title "Clients"]
      [:div.is-pulled-right
       [:a.button.is-primary.is-large {:on-click (dispatch-event [::new])} "New client"]]
      [clients-table]])])


(defn new-client-form []
  (let [{error :error new-client :data } @(re-frame/subscribe [::form ::new-client])]
    [:form
     [:h1.title.is-2 "Add client"]
     [:div.field
      [:label.label  "Name"]
      [:div.control
       [bind-field
        [:input.input {:type "text"
                       :field :name
                       :spec ::entity/name
                       :event ::change-new
                       :subscription new-client}]]]]

     [:div.field
      [:label.label "Client code"]
      [:div.control
       [bind-field
        [:input.input {:type "code"
                       :field :code
                       :spec ::entity/code
                       :event ::change-new
                       :subscription new-client}]]]]

     [:div.field
      [:label.label "Email"]
      [:div.control
       [bind-field
        [:input.input {:type "email"
                       :field :email
                       :spec ::entity/email
                       :event ::change-new
                       :subscription new-client}]]]]

     [:div.field
      [:label.label "Locations"]
      [:div.control
       [:div.field.has-addons
        [:p.control
         [bind-field
          [:input.input {:type "text"
                         :field :location
                         :event ::change-new
                         :subscription new-client}]]]
        [:p.control [:button.button.is-primary {:on-click (dispatch-event [::add-new-location])} "Add"]]]
       [:ul
        (for [location (:locations new-client)]
          ^{:key location} [:li  location ])]]]

     [:section.section {:style {:padding-bottom "0.75em" :padding-top "0.75em"}}
      [:h2.subtitle "Address"]
      [address-field {:field [:address]
                      :event ::change-new
                      :subscription new-client}]]

     [:section.section  {:style {:padding-bottom "0.75em" :padding-top "0.75em" :background "hsl(0, 0%, 96%)"}}
      [:h2.subtitle "Add bank account"]
      [:label.label "General"]
      [horizontal-field
       nil
       [:div.control
        [:p.help "Account Code"]
        [:div.field.has-addons.is-extended
         [:p.control [:a.button.is-static (:code new-client) "-" ]]
         [:p.control
          [bind-field
           [:input.input {:type "code"
                          :field [:new-account :code]
                          :spec ::entity/code
                          :event ::change-new
                          :subscription new-client}]]]]] 

       [:div.control
        [:p.help "Nickname"]
        [bind-field
         [:input.input {:placeholder "BOA Checking #1"
                        :type "text"
                        :field [:new-account :name]
                        :event ::change-new
                        :subscription new-client}]]]]

      [:label.label "Bank"]
      [horizontal-field
       nil
       [:div.control
        [:p.help "Bank Name"]
        [bind-field
         [:input.input {:placeholder "Bank of America"
                        :type "text"
                        :field [:new-account :bank-name]
                        :event ::change-new
                        :subscription new-client}]]]
       [:div.control
        [:p.help "Routing #"]
        [bind-field
         [:input.input {:placeholder "104819123"
                        :type "text"
                        :field [:new-account :routing]
                        :event ::change-new
                        :subscription new-client}]]]
       [:div.control
        [:p.help "Bank code"]
        [bind-field
         [:input.input {:placeholder "12/10123"
                        :type "text"
                        :field [:new-account :bank-code]
                        :event ::change-new
                        :subscription new-client}]]]]

      [:label.label "Checking account"]
      [horizontal-field
       nil
       [:div.control
        [:p.help "Account #"]
        [bind-field
         [:input.input {:placeholder "123456789"
                        :type "text"
                        :field [:new-account :number]
                        :event ::change-new
                        :subscription new-client}]]]
       [:div.control
        [:p.help "Check Number"]
        [bind-field
         [:input.input {:placeholder "10000"
                        :type "text"
                        :field [:new-account :check-number]
                        :event ::change-new
                        :subscription new-client}]]]]

      [:div.field
       [:label.label "Yodlee Account"]
       [:div.control
        [bind-field
         [:input.input {:placeholder "Yodlee Account #"
                        :type "text"
                        :field [:new-account :yodlee-account-id]
                        :event ::change-new
                        :subscription new-client}]]]
       ]
      [:div.field
       [:div.control
        #_(println (s/explain-data ::entity/bank-account (:new-account new-client)))
        [:button.button.is-primary {:disabled (if (and (doto (s/valid? ::entity/bank-account (:new-account new-client)) println)
                                                       (not ((set (map :code (:new-bank-accounts new-client)))
                                                             (str (:code new-client) "-" (-> new-client :new-account :code)))))
                                                ""
                                                "disabled")
                                    :on-click (dispatch-event [::add-new-bank-account])} "Add"]]]]

     [:section.section  {:style {:padding-bottom "0.75em" :padding-top "0.75em"}}
      [:h2.subtitle "Bank Accounts"]
      [:div.field
       nil
       [:div.control
        [:ul
         
         (for [{:keys [code name number check-number id]} (:bank-accounts new-client)]
           (if id
             ^{:key code}
             [:li code ": " name]
             ^{:key code} 
             [:li [:strong "* " code ": " name] [:button.button {:on-click (dispatch-event [::remove-new-bank-account code])} [:span.icon [:i.fa.fa-times]]]]))
         (for [[index {:keys [name code number check-number]}] (map vector (range) (:new-bank-accounts new-client))]
           ^{:key index} [:li [:strong "* " code ": " name] [:button.button {:on-click (dispatch-event [::remove-new-bank-account index])} [:span.icon [:i.fa.fa-times]]]])]]]]
     
     #_(when (:saving? new-client) [:div.is-overlay {:style {"backgroundColor" "rgba(150,150,150, 0.5)"}}])
     #_(println (s/explain-data ::entity/client new-client))

     (when error
       [:div.notification.is-warning.animated.fadeInUp
        error])
     
     [:submit.button.is-large.is-primary {:disabled (if (s/valid? ::entity/client new-client)
                                                      ""
                                                      "disabled")
                                          :on-click (dispatch-event [::save-new-client])
                                          :class (str @(re-frame/subscribe [::loading-class ::new-client]) (when error " animated shake"))} "Save"]]))

(defn admin-clients-page []
  (let [{:keys [adding-client?]} @(re-frame/subscribe [::subs/admin])]
    [side-bar-layout {:side-bar [admin-side-bar {}]
                      :main [admin-clients-content]
                      :right-side-bar-visible? adding-client?
                      :right-side-bar [new-client-form]}]))
