(ns auto-ap.views.components.expense-accounts-field
  (:require [auto-ap.forms :as forms]
            [auto-ap.subs :as subs]
            [auto-ap.views.components.typeahead :refer [typeahead-v3]]
            [auto-ap.views.components.money-field :refer [money-field]]
            [auto-ap.views.utils :refer [bind-field dispatch-event ->$]]
            [goog.string :as gstring]
            [re-frame.core :as re-frame]
            [clojure.string :as str]))

(defn can-replace-with-default? [accounts]
  (and (or (not (seq accounts))
           (<= 1 (count accounts)))
       (not (get-in accounts [0 :account :id]))))

(defn default-account [accounts default-account amount locations]
  [{:id (get-in accounts [0 :id]
                (str "new-" (random-uuid)))
    :amount (Math/abs amount)
    :amount-percentage 100
    :amount-mode "%"
    :location (or
               (:location default-account)
               (get-in accounts [0 :account :location])
               (if (= 1 (count locations))
                 (first locations)
                 nil))
    :account default-account}])


(defn from-graphql [accounts accounts-by-id total locations]
  (if (seq accounts)
    (vec (map
          (fn [a]
            (-> a 
                (update :account (fn [a]
                                   (accounts-by-id (:id a))))

                (update :amount js/parseFloat)
                (assoc :amount-percentage  (* 100 (/ (js/parseFloat (:amount a))
                                                     (Math/abs total))))
                (assoc :amount-mode "$")))
          accounts))
    [{:id (str "new-" (random-uuid))
      :amount-mode "$"
      :amount (Math/abs total)
      :amount-percentage 100
      :location (if (= 1 (count locations))
                  (first locations)
                  nil)}]))


;; EVENTS

(re-frame/reg-event-fx
 ::add-expense-account
 (fn [_ [_ event expense-accounts locations]]
   {:dispatch (conj event (conj expense-accounts
                                {:amount 0 :id (str "new-" (random-uuid))
                                 :amount-mode "%"
                                 :amount-percentage 0
                                 :location (if (= 1 (count locations))
                                             (first locations)
                                             nil)}))}))

(re-frame/reg-event-fx
 ::remove-expense-account
 (fn [_ [_ event expense-accounts id]]
   {:dispatch (conj event (transduce (filter
                                      (fn [ea]
                                        (not= (:id ea) id)) )
                                     conj
                                     []
                                     expense-accounts))}))

(defn recalculate-amounts [expense-accounts total]
  (mapv
   (fn [ea]
     (assoc ea :amount 
            (js/parseFloat
             (goog.string/format "%.2f"
                                 (* (/ (js/parseFloat (:amount-percentage ea)) 100.0) total)))))
   expense-accounts))

(re-frame/reg-event-fx
 ::spread-evenly
 (fn [_ [_ event expense-accounts max-value]]
   {:dispatch (into event [(recalculate-amounts (mapv
                                                 (fn [ea]
                                                   (assoc ea :amount-percentage (js/parseFloat
                                                                                (goog.string/format "%.2f"
                                                                                                    (* 100 (/ 1 (count expense-accounts)))))))
                                                 expense-accounts)
                                                max-value)])}))
(re-frame/reg-event-fx
 ::expense-account-changed
 (fn [_ [_ event expense-accounts max-value field value]]
   (println field value)
   (let [updated-accounts (cond-> expense-accounts
                              true (assoc-in field value)
                              (= (list :account) (drop 1 field)) (assoc-in [(first field) :location]  nil)

                              (= (list :account :id) (drop 1 field)) (-> (assoc-in [(first field) :account]  @(re-frame/subscribe [::subs/account nil value]))
                                                                         (assoc-in [(first field) :location]  nil))
                               
                              (= (list :amount-percentage) (drop 1 field)) (assoc-in [(first field) :amount]
                                                                                     (js/parseFloat
                                                                                      (goog.string/format "%.2f"
                                                                                                          (* (/ (cond-> value
                                                                                                                  (not (float? value)) (js/parseFloat )) 100.0)
                                                                                                             (cond-> max-value
                                                                                                               (not (float? max-value)) (js/parseFloat)))))))
         updated-accounts (if-let [location (get-in updated-accounts [(first field) :account :location])]
                            (assoc-in updated-accounts [(first field) :location] location)
                            updated-accounts)]
     (println updated-accounts)
     {:dispatch (into event [updated-accounts])})))


;; VIEWS
(defn expense-accounts-field [{expense-accounts :value client :client max-value :max locations :locations event :event descriptor :descriptor disabled :disabled percentage-only? :percentage-only? :or {percentage-only? false}}]
  (let [chooseable-expense-accounts @(re-frame/subscribe [::subs/accounts client])
        accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id client])]
    [:div
     [:div.columns
      [:div.column
       [:h1.subtitle.is-4.is-inline (str/capitalize descriptor) "s"]
       (when-not percentage-only?
         [:p.help "Remaining " (->$ (- max-value (reduce + 0 (map (comp js/parseFloat :amount) expense-accounts))))])]
      [:div.column.is-narrow
       (when-not disabled 
         [:p.buttons
          [:a.button {:on-click (dispatch-event [::spread-evenly event expense-accounts max-value])} "Spread evenly"]
          [:a.button {:on-click (dispatch-event [::add-expense-account event expense-accounts locations])} "Add"]])]]

     (for [[index {:keys [account id location amount amount-percentage amount-mode] :as expense-account}] (map vector (range) expense-accounts)
           :let [account (accounts-by-id (:id account))]]
       ^{:key id}
       [:div.box
        [:div.columns
         [:div.column
          [:h1.subtitle.is-6 (cond (and account (not percentage-only?))
                                   (str (:name account) " - "
                                        location ": "
                                        (gstring/format "$%.2f" (or amount 0) ))

                                   account
                                   (str (:name account) " - "
                                        location ": %"
                                        amount-percentage)

                                   :else
                                   [:i "New " descriptor])]]
         [:div.column.is-narrow
          (when-not disabled
            [:a.delete {:on-click (dispatch-event [::remove-expense-account event expense-accounts id])}])]]
        
        [:div.field
         [:div.columns
          [:div.column
           [:p.help "Account"]
           [:div.control.is-fullwidth
            [bind-field
             ^{:key (:id client)}
             [typeahead-v3 {:entities chooseable-expense-accounts
                            :entity->text (fn [x ]
                                           (str (:numeric-code x) " - " (:name x)))
                            :disabled disabled
                            :type "typeahead-v3"
                            :field [index :account]
                            :event [::expense-account-changed event expense-accounts max-value]
                            :subscription expense-accounts}]]]]
          [:div.column.is-narrow
           [:p.help "Location"]
           [:div.control
            (if-let [forced-location (:location account)]
              [:div.select
               [:select {:disabled "disabled" :style {:width "5em"} :value forced-location} [:option {:value forced-location} forced-location]]]
              [:div.select
               [bind-field
                [:select {:type "select"
                          :disabled (boolean (or (:location account)
                                                 disabled))
                          :style {:width "5em"}
                          :field [index :location]
                          :allow-nil? true
                          :spec (set locations)
                          :event [::expense-account-changed event expense-accounts max-value]
                          :subscription expense-accounts}
                 (map (fn [l] ^{:key l}  [:option {:value l} l]) locations)]]])]]]]
        
        [:div.field 
         [:p.help "Amount"]
         [:div.control
          [:div.field.has-addons.is-extended
           [:p.control [:span.select
                        [bind-field 
                         [:select {:type "select"
                                   :disabled (or disabled percentage-only?)
                                   :field [index :amount-mode]
                                   :allow-nil? false
                                   :event [::expense-account-changed event expense-accounts max-value]
                                   :subscription expense-accounts}
                          [:option "$"]
                          [:option "%"]]]]]
           [:p.control
            (if (= "$" amount-mode)
              [bind-field
               [:input.input {:type "number"
                              :field [index :amount]
                              :style {:text-align "right" :width "7em"}
                              :event [::expense-account-changed event expense-accounts max-value]
                              :disabled disabled
                              :subscription expense-accounts
                              :precision 2
                              :value (get-in expense-account [:amount])
                              :max max-value
                              :step "0.01"}]]
              [bind-field
               [:input.input {:type "number"
                              :field [index :amount-percentage]
                              :style {:text-align "right" :width "7em"}
                              :disabled disabled
                              :event [::expense-account-changed event expense-accounts max-value]
                              :precision 2
                              :subscription expense-accounts
                              :value (get-in expense-account [:amount-percentage])
                              :max "100"
                              :step "0.01"}]])]]]]])]))
