(ns auto-ap.views.components.expense-accounts-dialog
  (:require [re-frame.core :as re-frame]
            [reagent.core :as r]
            [clojure.string :as str]
            [clojure.spec.alpha :as s]
            [cljs-time.core :as c]
            [goog.string :as gstring]
            [auto-ap.subs :as subs]
            [auto-ap.entities.clients :as client]
            [auto-ap.entities.invoice :as invoice]
            [auto-ap.entities.vendors :as vendor]
            [auto-ap.expense-accounts :as expense-accounts]
            [auto-ap.entities.invoices-expense-accounts :as invoices-expense-accounts]
            [auto-ap.views.components.modal :refer [modal action-modal]]
            [auto-ap.views.utils :refer [dispatch-event bind-field horizontal-field date->str str->date pretty standard]]
            [auto-ap.utils :refer [by replace-if]]
            [auto-ap.views.components.typeahead :refer [typeahead]]
            [auto-ap.events :as events]))

(re-frame/reg-sub
 ::change-expense-accounts
 (fn [db]
   (-> db ::change-expense-accounts)))

(re-frame/reg-sub
 ::expense-account
 :<- [::change-expense-accounts]
 (fn [{{:keys [expense-accounts] :or {expense-accounts [] }} :invoice} [_ expense-account-id]]
   (first (filter #(= expense-account-id (:id %)) expense-accounts))))

(re-frame/reg-event-fx
 ::change-expense-accounts
 (fn [{:keys [db]} [_ i]]
   {:dispatch [::events/modal-status ::change-expense-accounts {:visible? true}]
    :db (assoc-in db [::change-expense-accounts :invoice] i)}))


(re-frame/reg-event-db
 ::change
 (fn [db [_ id f v]]
   (update-in db [::change-expense-accounts :invoice :expense-accounts]
              (fn [expense-accounts]
                (mapv
                 (fn [expense-account]
                   (if (= id (:id expense-account))
                     (assoc-in expense-account f v)
                     expense-account))
                 expense-accounts)))))

(re-frame/reg-event-fx
 ::change-expense-accounts-saving
 (fn [{:keys [db]} [_ on-success id ]]
   (let [{{:keys [expense-accounts total]} :invoice} @(re-frame/subscribe [::change-expense-accounts]) 
         expense-accounts-total (->> expense-accounts
                                     (map :new-amount)
                                     (map js/parseFloat)
                                     (map #(or % 0.0))
                                     (map #(if (js/Number.isNaN %)
                                             0.0
                                             %))
                                     (reduce + 0))
         does-add-up? (< (Math/abs (- expense-accounts-total (js/parseFloat total))) 0.001)]
     (if (and does-add-up?
              (every? :new-amount expense-accounts)
              (s/valid? (s/* ::invoices-expense-accounts/invoices-expense-account) expense-accounts))
       
       {:dispatch [::change-expense-accounts-submit on-success id]}
       {:dispatch [::events/modal-status ::change-expense-accounts {:saving? false :error-message "Expense accounts do not add up."}]}))))

(re-frame/reg-event-db
 ::saving-error
 (fn [db [_ message]]
   (assoc-in db [::change-expense-accounts :error] message)))

(re-frame/reg-event-fx
 ::change-expense-accounts-submit
 (fn [{:keys [db] } [_ on-success id]]
   (let [{:keys [id expense-accounts]} (get-in db [::change-expense-accounts :invoice])]
     {:graphql
      {:token (-> db :user)
       :query-obj {:venia/operation {:operation/type :mutation
                                     :operation/name "EditExpenseAccounts"}
                   
                   :venia/queries [{:query/data [:edit-expense-accounts
                                                 {:invoice-id  id
                                                  :expense-accounts (map (fn [ea] {:id (when-not (string? (:id ea)) (:id ea))
                                                                                   :amount (:new-amount ea)
                                                                                   :location (:location ea)
                                                                                   :expense-account-id (:expense-account-id ea)})
                                                                         expense-accounts)}
                                                 [:id :total :outstanding-balance :invoice-number :date
                                                  [:vendor [:name :id]]
                                                  [:expense_accounts [:amount :id :location :expense_account_id
                                                                      [:expense_account [:id :name [:parent [:id :name]]]]]]
                                                  [:client [:name :id :locations]]
                                                  [:payments [:amount :id [:payment [:amount :s3_url :check_number ]]]]]]}]}  
       :on-success on-success}})))

(re-frame/reg-event-db
 ::add-expense-account-split
 (fn [db _]
   (let [{{{:keys [locations]} :client} :invoice} @(re-frame/subscribe [::change-expense-accounts])]
     (update-in db [::change-expense-accounts :invoice :expense-accounts]
                conj {:amount "0.0" :id (str (random-uuid))  :expense-account-id {} :location (first locations)}))))

(re-frame/reg-event-db
 ::remove-expense-account-split
 (fn [db [_ x]]
   (update-in db [::change-expense-accounts :invoice :expense-accounts]
              (fn [expense-accounts]
                (vec (filter #(not= x (:id %)) expense-accounts) )))))


(defn change-expense-accounts-modal [{:keys [updated-event]}]
  (let [{{:keys [expense-accounts total] :or {expense-accounts [] total 0} {:keys [locations]} :client} :invoice error :error :as data} @(re-frame/subscribe [::change-expense-accounts])
        multi-location? (> (count locations) 1)
        change-event [::change]
        chooseable-expense-accounts @(re-frame/subscribe [::subs/chooseable-expense-accounts])
        expense-accounts-total (->> expense-accounts
                                    (map :new-amount)
                                    (map js/parseFloat)
                                    (map #(or % 0.0))
                                    (map #(if (js/Number.isNaN %)
                                            0.0
                                            %))
                                    (reduce + 0))]
    [action-modal {:id ::change-expense-accounts
                   :title "Change expense accounts"
                   :action-text "Save"
                   :save-event [::change-expense-accounts-saving updated-event]
                   :can-submit? true}
     (when error
     [:div error] )

     [:div
      [:a.button.is-primary {:on-click (dispatch-event [::add-expense-account-split])} "Add split"]]
     [:table.table
      [:thead
       [:tr
        [:th {:style {:width "500px"}} "Expense Account"]
        (when multi-location?
          [:th {:style {:width "200px"}} "Location"])
        [:th {:style {:width "200px"}} "Original Amount"]
        [:th {:style {:width "300px"}} "Amount"]
        [:th {:style {:width "5em"}}]]]
      [:tbody
       (doall (for [{:keys [id] :as expense-account} expense-accounts
                    :let [sub @(re-frame/subscribe [::expense-account (:id expense-account)])]]
                ^{:key id}
                [:tr
                 [:td.expandable [:div.control
                                  [bind-field
                                   [typeahead {:matches (map (fn [x] [(:id x) (str (:id x) " - " (:name x))]) chooseable-expense-accounts)
                                               :type "typeahead"
                                               :field [:expense-account-id]
                                               :event [::change id]
                                               :spec ::invoices-expense-accounts/expense-account-id
                                               :subscription sub}]]]]

                 (when multi-location?
                   [:td
                    (if-let [forced-location (-> expense-account :expense-account-id expense-accounts/expense-accounts :location)]
                      [:div.select
                       [:select {:disabled "disabled" :value forced-location} [:option {:value forced-location} forced-location]]]
                      [:div.select
                       [bind-field
                        [:select {:type "select"
                                  :field [:location]
                                  :spec (set locations)
                                  :event [::change id]
                                  :subscription sub}
                         (map (fn [l] ^{:key l}  [:option {:value l} l]) locations)]]])])
                 
                 [:td
                  (str "$" (get-in sub [:amount]))]
                 [:td
                  [:div.control
                   [:div.field.has-addons.is-extended
                    [:p.control [:a.button.is-static "$"]]
                    [:p.control
                     [bind-field
                      [:input.input {:type "number"
                                     :field [:new-amount-temp]
                                     :style {:text-align "right"}
                                     :on-blur (dispatch-event [::change id [:new-amount] (:new-amount-temp sub)])
                                     :on-key-down (fn [e ]
                                                    (if (= 13 (.-keyCode e))
                                                      (do 
                                                        
                                                        (re-frame/dispatch [::change id [:new-amount] (:new-amount-temp sub)])
                                                        true)
                                                      false))
                                     :event [::change id]
                                     :subscription sub
                                     :value (get-in data [:new-amount-temp])
                                     
                                     :max (:total data)
                                     :step "0.01"}]]]]]]
                 [:td [:a.button {:on-click (dispatch-event [::remove-expense-account-split (:id expense-account)])} [:i.fa.fa-times]]]]))
       [:tr
        [:td.no-border { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Invoice total: "]
        [:td.no-border { :style { :text-align "right"} } (str (gstring/format "$%.2f" total ) )]]
       [:tr
        [:td { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Account total: "]
        [:td { :style { :text-align "right"} } (str (gstring/format "$%.2f" expense-accounts-total ) )]]
       [:tr
        [:td.no-border { :col-span (if multi-location? "3" "2") :style { :text-align "right"} } "Difference: "]
        [:td.no-border { :style { :text-align "right"} } (str (gstring/format "$%.2f" (- total expense-accounts-total) ) )]]]]]))
