(ns auto-ap.views.pages.admin.clients.form
  (:require [auto-ap.entities.clients :as entity]
            [auto-ap.forms :as forms]
            [auto-ap.subs :as subs]
            [auto-ap.events :as events]
            [auto-ap.views.components.address :refer [address-field]]
            [auto-ap.views.components.typeahead :refer [typeahead-v3]]
            [auto-ap.views.components.layouts :refer [side-bar]]
            [auto-ap.views.utils
             :refer
             [date->str
              date-picker
              dispatch-event
              horizontal-field
              multi-field
              standard]]
            [cljs-time.coerce :as coerce]
            [cljs-time.core :as t]
            [clojure.string :as str]
            [re-frame.core :as re-frame]
            [reagent.core :as r]
            [react-signature-canvas :as canvas]))

(def signature-canvas (r/adapt-react-class (.-default canvas)))

(defn upload-replacement-button [{:keys [on-change]} text]
  (let [button (atom nil)]
    (r/create-class {:display-name "Upload button"
                     :reagent-render 
                     (fn []
                       [:<> 
                        [:label.button {:for "upload_replacement_signature"} text]
                        [:input.button {:type "file" :id "upload_replacement_signature"
                                        :style {:display "none"}
                                        :on-change (fn []
                                                     (let [fr (js/FileReader.)]
                                                       (.addEventListener fr "load" (fn []
                                                                                      (on-change (.-result fr))))

                                                       (.readAsDataURL fr (aget (.-files @button) 0)))
                                                     )
                                        :ref (fn [i] (reset! button i))} ]])})))

(defn signature [{:keys [signature-file signature-data on-change]}]
  (let [canvas (atom nil)
        edit-mode? (r/atom false)
        w (* 1.5 464)
        h (* 1.5 174)]
    (fn [{:keys [signature-file signature-data on-change]}]
      [:div 
       (if @edit-mode?
         [:div
          [signature-canvas {"canvasProps" {"width" w
                                            "height" h
                                            "style" #js {"border" "1px solid rgb(219,219,219)"
                                                         "border-radius" "4px"}}
                             "backgroundColor" "#FFF"
                             :ref (fn [el]
                                    (reset! canvas el))}]
          [:div.buttons 
           [:a.button.is-primary.is-outlined  {:on-click (fn []
                                                           (on-change (.toDataURL @canvas "image/jpeg"))
                                                           (reset! edit-mode? false))}
            "Accept"]
           [:a.button.is-warning.is-outlined  {:on-click (fn []
                                    (.clear @canvas)
                                    (reset! edit-mode? false))}
            "Cancel"]]]
         (if (or signature-data signature-file)
           [:div 
            [:img {:src (or signature-data signature-file)
                   :style {:width w
                           :height h}}]
            [:div.buttons 
             [:a.button {:on-click (fn []
                                     (reset! edit-mode? true))}
              "Replace Signature"]

             [upload-replacement-button {:on-change on-change} "Upload replacement"]]]
           [:div
            [:div.has-text-centered.is-vcentered {:style {:width w
                                                          :height h
                                                          :margin-bottom "8px"
                                                          :background "#EEE"}}
             "No signature"]
            [:div.buttons 
             [:a.button.is-primary.is-outlined {:on-click (fn []
                                     (reset! edit-mode? true))}
              "New Signature"]

             [upload-replacement-button {:on-change on-change} "Upload signature"]]]))
       ])))

(re-frame/reg-sub
 ::can-submit
 :<- [::new-client-request]
 (fn [r _]
   true
   
   #_(s/valid? ::entity/client r)))

(re-frame/reg-sub
 ::new-client-request
 :<- [::forms/form ::form]
 (fn [{new-client-data :data} _]
   (cond-> 
       {: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 (mapv :location (:locations new-client-data))
        :matches (mapv :match (:matches new-client-data))
        :location-matches (:location-matches new-client-data)
        :week-a-credits (:week-a-credits new-client-data)
        :week-a-debits (:week-a-debits new-client-data)
        :week-b-credits (:week-b-credits new-client-data)
        :week-b-debits (:week-b-debits 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))}
        :signature-data (:signature-data new-client-data)
        :forecasted-transactions (map (fn [{:keys [id day-of-month identifier amount]}]
                                        {:id id
                                         :day-of-month (js/parseInt day-of-month)
                                         :identifier identifier
                                         :amount amount})
                                      (:forecasted-transactions new-client-data))
        :bank-accounts (map (fn [{:keys [number name check-number intuit-bank-account include-in-reports type id code numeric-code start-date bank-name routing bank-code new? sort-order visible yodlee-account-id locations yodlee-account]}]
                              (println intuit-bank-account)
                              {:number number
                               :name name
                               :check-number (when-not (str/blank? check-number)
                                               (js/parseInt check-number))
                               :numeric-code (when-not (str/blank? numeric-code)
                                               (js/parseInt numeric-code))
                               :include-in-reports include-in-reports
                               :start-date (cond (not start-date)
                                                 nil

                                                 (instance? goog.date.Date start-date)
                                                 (date->str start-date standard)

                                                 :else
                                                 start-date
                                                 )
                               :type type
                               :id id
                               :sort-order sort-order
                               :visible visible
                               :locations (mapv :location locations)
                               :yodlee-account-id (when-not (str/blank? yodlee-account-id)
                                                    (js/parseInt yodlee-account-id))
                               :yodlee-account (:id yodlee-account)
                               :intuit-bank-account (:id intuit-bank-account)
                               :code (if new?
                                       (str (:code new-client-data) "-" code)
                                       code)
                               :bank-name bank-name
                               :routing routing
                               :bank-code bank-code})
                            (:bank-accounts new-client-data))})))

(re-frame/reg-event-fx
 ::editing
 (fn  [{:keys [db]} [_ client-id]]
   {:db (-> db
            (forms/stop-form ::form)
            (forms/start-form ::form (-> (get (:clients db) client-id)
                                         (update :locations #(mapv (fn [l] {:location l}) %))
                                         (update :matches #(mapv (fn [l] {:match l}) %))
                                         (update :bank-accounts
                                                 (fn [bas]
                                                   (mapv (fn [ba]
                                                           (update ba :locations (fn [ls]
                                                                                   (map (fn [l] {:location l})
                                                                                        ls))))
                                                         bas))))))}))

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

   (let [new-client-req @(re-frame/subscribe [::new-client-request])
         user @(re-frame/subscribe [::subs/token])]
     
     {:db (-> new-client-form)
      :graphql
      {:token user
       :owns-state {:single ::form}
       :query-obj {:venia/operation {:operation/type :mutation
                                     :operation/name "EditClient"}
                   :venia/queries [{:query/data [:edit-client
                                                 {:edit-client new-client-req}
                                                 (events/client-query user)]}]}
       :on-success [::save-complete]
       :on-error [::forms/save-error ::form]}})))
(re-frame/reg-event-db
 ::save-complete
 (fn  [db [_ client]]
   (-> db
       (forms/stop-form ::form)
       
       (assoc-in [:clients (:id (:edit-client client))] (update (:edit-client client) :bank-accounts (fn [bas] (->> bas (sort-by :sort-order) vec)))))))



(re-frame/reg-event-db
 ::add-new-bank-account
 [(forms/in-form ::form) (re-frame/path [:data])]
 (fn [client [_ type]]
   (update client :bank-accounts conj {:type type :active? true :new? true :visible true :sort-order (count (:bank-accounts client))})))

(re-frame/reg-event-db
 ::bank-account-activated
 [(forms/in-form ::form) (re-frame/path [:data :bank-accounts])]
 (fn [bank-accounts [_ index]]
   (update (vec (sort-by :sort-order bank-accounts)) index assoc :active? true)))

(re-frame/reg-event-db
 ::bank-account-deactivated
 [(forms/in-form ::form) (re-frame/path [:data :bank-accounts])] 
 (fn [bank-accounts [_ index]]
   (update (vec (sort-by :sort-order bank-accounts)) index assoc :active? false)))

(re-frame/reg-event-db
 ::bank-account-removed
 [(forms/in-form ::form) (re-frame/path [:data :bank-accounts])]
 (fn [bank-accounts [_ index]]
   (vec (concat (take index bank-accounts)
                (drop (inc index) bank-accounts)))))

(re-frame/reg-event-db
 ::sort-swapped
 [(forms/in-form ::form) (re-frame/path [:data :bank-accounts])]
 (fn [bank-accounts [_ source dest]]
   (->> (-> bank-accounts
            (assoc-in [source :sort-order] (get-in bank-accounts [dest :sort-order]))
            (assoc-in [dest :sort-order] (get-in bank-accounts [source :sort-order]))

            )
        (sort-by :sort-order)
        vec)))

(re-frame/reg-event-db
 ::toggle-visible
 [(forms/in-form ::form) (re-frame/path [:data :bank-accounts])]
 (fn [bank-accounts [_ account]]
   (-> (->> bank-accounts
            (sort-by :sort-order)
            vec)
       (update-in [account :visible] #(not %)))))




(def client-form 
  (forms/vertical-form {:can-submit [::can-submit]
                        :change-event [::forms/change ::form]
                        :submit-event [::save-new-client ]
                        :id ::form}))

(def first-week-a (coerce/to-date-time #inst "1999-12-27T00:00:00.000-07:00"))

(defn is-week-a? [d]
  (= 0 (mod (t/in-weeks (t/interval first-week-a d)) 2)))


(re-frame/reg-sub
 ::yodlee-accounts
 :<- [::subs/clients-by-id]
 (fn [clients [_ id]]
   
   (if id
     (mapcat :accounts (:yodlee-provider-accounts (get clients id) ))
     [])))


(defn bank-account-card [new-client {:keys [active? new? type visible code name number check-number id sort-order] :as bank-account} first? last?]
  (let [{:keys [form field raw-field error-notification submit-button ]} client-form]
    [:div.card {:style {:margin-bottom "1em"}}
     [:header.card-header
      [:div.card-header-title {:style {:text-overflow "ellipsis"}}
       [:div.level {:style {:width "100%"}}
        [:div.level-left
         [:div.level-item 
          [:span.icon.inline
           (cond
             (#{:check ":check"} type) [:span.icon-check-payment-sign]

             (#{:credit ":credit"} type) [:span.icon-credit-card-1]

             :else [:span.icon-accounting-bill])]]
         [:div.level-item code ": " name]]
        [:div.level-right
         [:div.level-item
          [:div.buttons
           [:a.button {:on-click (dispatch-event [::toggle-visible sort-order])} [:span.icon (if visible
                                                                                               [:span.fa.fa-eye]
                                                                                               [:span.fa.fa-eye-slash]
                                                                                               )]]
           (when-not last?
             [:a.button {:on-click (dispatch-event [::sort-swapped sort-order (inc sort-order)])} [:span.icon [:span.fa.fa-sort-down]]])
           (when-not first?
             [:a.button {:on-click (dispatch-event [::sort-swapped sort-order (dec sort-order)])} [:span.icon [:span.fa.fa-sort-up]]])]]]]]
      (if active?
        [:a.card-header-icon
         {:on-click (dispatch-event [::bank-account-deactivated sort-order])}
         [:span.icon
          [:span.fa.fa-angle-up]]]
        [:a.card-header-icon
         {:on-click (dispatch-event [::bank-account-activated sort-order])}
         [:span.icon
          [:span.fa.fa-angle-down]]])]
     (when active?
       [:div.card-content  
        [:label.label "General"]
        [horizontal-field
         nil
         [:div.control
          [:p.help "Account Code"]
          (if new?
            [:div.field.has-addons.is-extended
             [:p.control [:a.button.is-static (:code new-client) "-" ]]
             [:p.control
              [raw-field
               [:input.input {:type "code"
                              :field [:bank-accounts sort-order :code]
                              :spec ::entity/code}]]]]
            [:div.field [:p.control code]])] 

         

         [field "Nickname"
          [:input.input {:placeholder "BOA Checking #1"
                         :type "text"
                         :field [:bank-accounts sort-order :name]}]]
         [horizontal-field
          nil
          [field "Numeric Code (for finiancials)"
           [:input.input {:placeholder "20101"
                          :type "text"
                          :field [:bank-accounts sort-order :numeric-code]}]]]
         [field "Start date"
          [date-picker {:class-name "input"
                        :class "input"
                        :format-week-number (fn [] "")
                        :previous-month-button-label ""
                        :placeholder "mm/dd/yyyy"
                        :next-month-button-label ""
                        :next-month-label ""
                        :type "date"
                        :field [:bank-accounts sort-order :start-date]}]]]
        (when (#{:check ":check"} type )
          [:div

           [:label.label "Bank"]
           [horizontal-field
            nil
            [field "Bank Name"
             [:input.input {:placeholder "Bank of America"
                            :type "text"
                            :field [:bank-accounts sort-order :bank-name]}]]
            [field "Routing #"
             [:input.input {:placeholder "104819123"
                            :type "text"
                            :field [:bank-accounts sort-order :routing]}]]
            [field "Bank code"
             [:input.input {:placeholder "12/10123"
                            :type "text"
                            :field [:bank-accounts sort-order :bank-code]}]]]

           [:label.label "Checking account"]
           [horizontal-field
            nil
            [field "Account #"
             [:input.input {:placeholder "123456789"
                            :type "text"
                            :field [:bank-accounts sort-order :number]}]]

            [field "Check Number"
             [:input.input {:placeholder "10000"
                            :type "text"
                            :field [:bank-accounts sort-order :check-number]}]]]
           [field "Yodlee Account"
            [:input.input {:placeholder "Yodlee Account #"
                           :type "text"
                           :field [:bank-accounts sort-order :yodlee-account-id]}]]
           [field "Yodlee Account (new)"
            [typeahead-v3 {:entities @(re-frame/subscribe [::yodlee-accounts (:id new-client)])
                           :entity->text (fn [m] (str (:name m) " - " (:number m)))
                           :type "typeahead-v3"
                           :field [:bank-accounts sort-order :yodlee-account]}]]
           [field "Intuit Bank Account"
            [typeahead-v3 {:entities @(re-frame/subscribe [::subs/intuit-bank-accounts])
                           :entity->text (fn [m] (str (:name m)))
                           :type "typeahead-v3"
                           :field [:bank-accounts sort-order :intuit-bank-account]}]]])

        (when (#{:credit ":credit"} type )
          [:div

           
           [:label.label "Account"]
           [horizontal-field
            nil
            [field "Bank Name"
             [:input.input {:placeholder "Bank of America"
                            :type "text"
                            :field [:bank-accounts sort-order :bank-name]}]]]
           

           [horizontal-field
            nil
            [field "Account #"
             [:input.input {:placeholder "123456789"
                            :type "text"
                            :field [:bank-accounts sort-order :number]}]]]

           [field "Yodlee Account"
            [:input.input {:placeholder "Yodlee Account #"
                           :type "text"
                           :field [:bank-accounts sort-order :yodlee-account-id]}]]
           [field "Yodlee Account (new)"
            [typeahead-v3 {:entities @(re-frame/subscribe [::yodlee-accounts (:id new-client)])
                           :entity->text (fn [m] (str (:name m) " - " (:number m)))
                           :type "typeahead-v3"
                           :field [:bank-accounts sort-order :yodlee-account]}]]
           [field "Intuit Bank Account"
            [typeahead-v3 {:entities @(re-frame/subscribe [::subs/intuit-bank-accounts])
                           :entity->text (fn [m] (str (:name m)))
                           :type "typeahead-v3"
                           :field [:bank-accounts sort-order :intuit-bank-account]}]]])
        [:div.field
            [:label.label "Locations"]
            [:div.control
             [:p.help "If this account is location-specific, add the valid locations"]
             [raw-field 
              [multi-field {:type "multi-field"
                            :field [:bank-accounts sort-order :locations]
                            :template [[:select.select {:type "select"
                                                        :style {:width "7em"}
                                                        :allow-nil? true
                                                        :field [:location]
                                                        :spec (set (map :location (get-in new-client [:locations])))}
                                        [:<>
                                         [:option ""]
                                         [:<> (map (fn [l] ^{:key (:location l)}
                                                 [:option {:value (:location l)} (:location l)])
                                               (get-in new-client [:locations]))]]]]}]]]]
        [:div.field 
         [:label.checkbox
          [raw-field
           [:input {:type "checkbox"
                    :field [:bank-accounts sort-order :include-in-reports]}]]
          " Include in reports"]]])

     (when active?
       [:footer.card-footer
        [:a.card-footer-item {:href "#" :on-click (dispatch-event [::bank-account-deactivated sort-order])} "Done"]
        (when new?
          [:a.card-footer-item.is-warning {:href "#" :on-click (dispatch-event [::bank-account-removed sort-order])} "Remove"])])]))

(defn new-client-form []
  (let [{new-client :data } @(re-frame/subscribe [::forms/form ::form])
        {:keys [form-inline field raw-field error-notification submit-button ]} client-form
        next-week-a (if (is-week-a? (t/now))
                      "This week"
                      "Next week")
        next-week-b (if (is-week-a? (t/now))
                      "Next week"
                      "This week")]
    [side-bar {:on-close   (dispatch-event [::forms/form-closing ::form ])}
     ^{:key (:id new-client)}
     [:div ;; div is important for actually unmounting for now
      (form-inline {:title "Add client"}
                   [:<>
                    (field "Name"
                           [:input.input {:type "text"
                                          :field [:name]
                                          :spec ::entity/name
                                          }])

                    
                    [:div.field
                     [:p.help "Client code"
                      ]
                     (if (:id new-client)
                       [:div.control
                        (:code new-client)]
                       [:div.control
                        (raw-field
                         [:input.input {:type "code"
                                        :field :code
                                        :spec ::entity/code}])])]
                    

                    (field "Email"
                           [:input.input {:type "email"
                                          :field :email
                                          :spec ::entity/email}])

                    [:div.field
                     [:p.help "Signature"]
                     [signature {:signature-file (:signature-file new-client)
                                 :signature-data (:signature-data new-client)
                                 :on-change (fn [uri]
                                              (re-frame/dispatch [::forms/change ::form [:signature-data] uri]))}]]

                    [:h2.subtitle.is-5 "Name matches"]
                    [:div.control
                     (raw-field 
                      [multi-field {:type "multi-field"
                                    :field :matches
                                    :template [[:input.input {:field [:match]
                                                              :placeholder "Harry's burger joint"
                                                              :style { :width "15em"}}]]}])]

                    
                    [:h2.subtitle "Locations"]
                    [:div.control
                     (raw-field 
                      [multi-field {:type "multi-field"
                                    :field :locations
                                    :allow-change? false
                                    :template [[:input.input {:field [:location]
                                                              :maxlength 2
                                                              :style { :width "4em"}}]]}])]

                    [:h2.subtitle "Location Matches"]
                    [:div.control
                     (raw-field 
                      [multi-field {:type "multi-field"
                                    :field :location-matches
                                    :template [[:input.input {:field [:match]
                                                              :placeholder "Downtown"
                                                              :style { :width "15em"}}]
                                               [:input.input {:field [:location]
                                                              :placeholder "DT"
                                                              :maxlength 2
                                                              :style { :width "4em"}}]]}])]

                    [:h2.subtitle "Address"]
                    [address-field {:field [:address]
                                    :event [::forms/change ::form]
                                    :subscription new-client}]
                    

                    [:h2.subtitle "Bank accounts"]
                    (for [bank-account (sort-by :sort-order (:bank-accounts new-client))]
                      ^{:key (:sort-order bank-account)}
                      [bank-account-card new-client bank-account (= 0 (:sort-order bank-account)) (= (:sort-order bank-account) (dec (count (:bank-accounts new-client))))])

                    [:div.columns
                     [:div.column.is-third
                      [:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :credit])} "Add Credit Account"]]
                     [:div.column.is-third
                      [:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :check])} "Add Checking Account"]]
                     [:div.column.is-third
                      [:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :cash])} "Add Cash Account"]]]

                    [:h2.subtitle "Cash flow"]
                    [:label.label (str "Week A (" next-week-a ")")]
                    [field "Regular Credits"
                     [:input.input {:type "number"
                                    :placeholder "500.00"
                                    :field [:week-a-credits]
                                    :step "0.01"}]]
                    [field "Regular Debits"
                     [:input.input {:type "number"
                                    :placeholder "150.00"
                                    :field [:week-a-debits]
                                    :step "0.01"}]]
                    [:label.label (str "Week B (" next-week-b ")")]
                    [field "Regular Credits"
                     [:input.input {:type "number"
                                    :placeholder "1000.00"
                                    :field [:week-b-credits]
                                    :step "0.01"}]]
                    [field "Regular Debits"
                     [:input.input {:type "number"
                                    :placeholder "250.00"
                                    :field [:week-b-debits]
                                    :step "0.01"}]]
                    [:div.field
                     [:label.label "Forecasted transactions"]

                     [:div.control
                      (raw-field 
                       [multi-field {:type "multi-field"
                                     :field :forecasted-transactions
                                     :template [[:input.input {:type "text"
                                                               :placeholder "Identifier"
                                                               :style {:width "10em"}
                                                               :field [ :identifier]}]
                                                [:input.input {:type "number"
                                                               :style {:width "4em"}
                                                               :placeholder "Day of month"
                                                               :step "1"
                                                               :field [:day-of-month]}]
                                                [:input.input {:type "number"
                                                               :placeholder "250.00"
                                                               :class "has-text-right"
                                                               :style {:width "7em"}
                                                               :field [:amount]
                                                               :step "0.01"}]]}])]]
                    (error-notification)
                    (submit-button "Save")])]]))
