(ns auto-ap.views.pages.admin.rules.form
  (:require [auto-ap.entities.transaction-rule :as entity]
            [auto-ap.events :as events]
            [auto-ap.forms :as forms]
            [auto-ap.subs :as subs]
            [auto-ap.views.components.button-radio :refer [button-radio]]
            [auto-ap.utils :refer [dollars=]]
            [auto-ap.views.components.dropdown :refer [drop-down]]
            [auto-ap.views.components.expense-accounts-field :as expense-accounts-field :refer [expense-accounts-field recalculate-amounts]]
            [auto-ap.views.components.layouts :as layouts]
            [auto-ap.views.components.typeahead :refer [typeahead-v3]]
            [auto-ap.views.pages.admin.rules.common :refer [default-read]]
            [auto-ap.views.pages.admin.rules.results-modal :as results-modal]
            [auto-ap.views.utils :refer [date->str date-picker dispatch-event standard with-user]]
            [cljs-time.core :as c]
            [clojure.spec.alpha :as s]
            [clojure.string :as str]
            [re-frame.core :as re-frame]
            [auto-ap.status :as status]))

;; SUBS

(re-frame/reg-sub
 ::default-note
 :<- [::forms/form ::form]
 (fn [{{:keys [client description amount-lte amount-gte dom-lte dom-gte]} :data}]
   
   (str/join " - "  (filter (complement str/blank?)
                            [(:code client)
                             description
                             (when (or amount-lte amount-gte)
                               (str (when amount-gte
                                      (str amount-gte "<"))
                                    "amt"
                                    (when amount-lte
                                      (str "<" amount-lte))))

                             (when (or dom-lte dom-gte)
                               (str (when dom-gte
                                      (str dom-gte "<"))
                                    "dom"
                                    (when dom-lte
                                      (str "<" dom-lte))))]))))

(re-frame/reg-sub
 ::can-submit
 :<- [::forms/form ::form]
 (fn [{:keys [data status]} _]
   (s/valid? ::entity/transaction-rule data)))

(re-frame/reg-sub
 ::query
 :<- [::forms/form ::form]
 (fn [{:keys [data] }]
   {:venia/operation {:operation/type :mutation
                      :operation/name "UpsertTransactionRule"}
    :venia/queries [{:query/data [:upsert-transaction-rule
                                  {:transaction-rule  (-> data
                                                          (select-keys [:id
                                                                        :description
                                                                        :transaction-approval-status
                                                                        :amount-lte
                                                                        :amount-gte
                                                                        :dom-lte
                                                                        :dom-gte
                                                                        :accounts
                                                                        :note])
                                                          (update :note #(if (str/blank? %)
                                                                           @(re-frame/subscribe [::default-note])
                                                                           %))
                                                          (assoc  :vendor-id (:id (:vendor data)))
                                                          (assoc  :yodlee-merchant-id (:id (:yodlee-merchant data)))
                                                          (update :description (fn [d] (when d (str/replace d #"\\" "\\\\"))))
                                                          (update :accounts (fn [as]
                                                                              (map #(-> %
                                                                                        (update :id (fn [i] (if (some-> i (str/starts-with? "new-"))
                                                                                                              nil
                                                                                                              i)))
                                                                                        (assoc :percentage (/ (get-in % [:amount-percentage]) 100 ))
                                                                                        (assoc :account-id (get-in % [:account :id]))
                                                                                        (select-keys [:percentage :id :location :account-id]))
                                                                                   as)))
                                                          (assoc :client-id (:id (:client data)))
                                                          (assoc :bank-account-id (:id (:bank-account data))))}
                                  default-read]}]}))

(defn ungraphql-transaction-rule [x]
  (-> x
      (update :amount-lte #(some-> % js/parseFloat))
      (update :amount-gte #(some-> % js/parseFloat))))

(re-frame/reg-sub
 ::test-query
 :<- [::forms/form ::form]
 (fn [{:keys [data] }]
   {:venia/operation {:operation/type :query
                      :operation/name "TestTransactionRule"}
    :venia/queries [{:query/data [:test-transaction-rule
                                  {:transaction-rule  (-> data
                                                          (select-keys [:id
                                                                        :description
                                                                        :amount-lte
                                                                        :amount-gte
                                                                        :dom-lte
                                                                        :dom-gte
                                                                        :note])
                                                          (update :description (fn [d] (when d (str/replace d #"\\" "\\\\"))))
                                                          (assoc  :yodlee-merchant-id (:id (:yodlee-merchant data)))
                                                          (assoc :client-id (:id (:client data)))
                                                          (assoc :bank-account-id (:id (:bank-account data))))}
                                  [:id
                                   :date
                                   :amount
                                   [:client [:name]]
                                   [:bank-account [:name]]
                                   :description-original]]}]}))



;; EVENTS

(re-frame/reg-event-db
 ::adding
 (fn [db [_ new]]
   (-> db (forms/start-form ::form (assoc new
                                          :description nil
                                          :client nil
                                          :bank-account nil
                                          :note nil
                                          :amount-lte nil
                                          :amount-gte nil
                                          :dom-lte nil
                                          :dom-gte nil
                                          :vendor nil
                                          :transaction-approval-status :unapproved
                                          :accounts [{:id (str "new-" (random-uuid))
                                                      :amount-mode "%"
                                                      :amount-percentage 100
                                                      :location nil}])))))

(re-frame/reg-event-db
 ::editing
 (fn [db [_ which]]
   (let [accounts-by-id @(re-frame/subscribe [::subs/all-accounts-by-id (:client which)])]
     (-> db (forms/start-form ::form (-> which
                                         (select-keys [:description
                                                       :id
                                                       :client
                                                       :bank-account
                                                       :note
                                                       :amount-lte
                                                       :amount-gte
                                                       :dom-lte
                                                       :dom-gte
                                                       :vendor
                                                       :accounts
                                                       :yodlee-merchant
                                                       :transaction-approval-status])
                                         (update :accounts (fn [xs]
                                                             (mapv #(-> %
                                                                        (assoc  :amount-percentage (* (:percentage %) 100.0))
                                                                        (update :account (fn [a]
                                                                                           (accounts-by-id (:id a)))))
                                                                   xs)))))))))


(re-frame/reg-event-db
 ::changed
 (forms/change-handler ::form
                       (fn  [data field value]
                         (cond  (and (= [:vendor] field)
                                     value
                                     (expense-accounts-field/can-replace-with-default? (:accounts data)))
                                [[:accounts] (expense-accounts-field/default-account (:accounts data)
                                                                                     @(re-frame/subscribe [::subs/vendor-default-account (:id value) (:client data)])
                                                                                     (:total data)
                                                                                     [])]

                                (= [:client] field)
                                [[:accounts] (expense-accounts-field/default-account (:accounts data)
                                                                                     @(re-frame/subscribe [::subs/vendor-default-account (:id (:vendor data)) value])
                                                                                     (:total data)
                                                                                     [])
                                 [:bank-account] nil]

                                :else
                                []))))

(re-frame/reg-event-fx
 ::saving
 [with-user (forms/triggers-loading ::form) (forms/in-form ::form)]
 (fn [{:keys [user] {:keys [data]} :db} [_ params]]
   {:graphql
    {:token user
     :query-obj @(re-frame/subscribe [::query])
     :on-success (fn [result]
                   [::updated (:upsert-transaction-rule result)])
     :on-error [::forms/save-error ::form]}}))

(re-frame/reg-event-fx
 ::test-clicked
 [with-user (forms/triggers-loading ::form) (forms/in-form ::form)]
 (fn [{:keys [user] {:keys [data]} :db} [_ params]]
   {:graphql
    {:token user
     :owns-state {:single ::test}
     :query-obj @(re-frame/subscribe [::test-query])
     :on-success [::succeeded-test]
     :on-error [::forms/save-error ::form]}}))

(re-frame/reg-event-fx
 ::updated
 [(forms/triggers-stop ::form)]
 (fn [{:keys [db]} [_ {:keys [rule-saved]} result]]
   {:db (forms/start-form db ::form {:client @(re-frame/subscribe [::subs/client])})
    :dispatch (conj rule-saved (:upsert-transaction-rule result))}))

(re-frame/reg-event-fx
 ::succeeded-test
 [(forms/triggers-stop-loading ::form)]
 (fn [{:keys [db]} [_ result]]

   {:dispatch [::results-modal/opening (:test-transaction-rule result) nil false]}))



;; VIEWS

(def rule-form (forms/vertical-form {:can-submit [::can-submit]
                                        :change-event [::changed]
                                        :submit-event [::saving ]
                                        :id ::form}))



(defn form [{:keys [can-change-amount?] :as params}]
  [layouts/side-bar {:on-close   (dispatch-event [::forms/form-closing ::form ])}
   (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form])
         {:keys [form-inline field raw-field error-notification submit-button ]} rule-form
         default-note @(re-frame/subscribe [::default-note])
         test-state @(re-frame/subscribe [::status/single ::test])
         exists? (:id data)]
     ^{:key id}
     (form-inline (assoc params :title "New Transaction Rule") 
           [:<>
            

            (field "Client"
                   [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients])
                                  :auto-focus true
                                  :entity->text :name
                                  :type "typeahead-v3"
                                  :field [:client]
                                  :spec ::entity/client}])

            
            (with-meta 
              (field "Bank account"
                     [typeahead-v3 {:entities @(re-frame/subscribe [::subs/real-bank-accounts-for-client (:client data)])
                                    :entity->text :name
                                    :type "typeahead-v3"
                                    :field [:bank-account]
                                    :spec ::entity/bank-account}])
              ;; TODO this forces unmounting when client changes, since it is an "uncontorlled" input
              {:key (str "client-" (:id (:client data)))})

            (field "Yodlee Merchant"
                   [typeahead-v3 {:entities @(re-frame/subscribe [::subs/yodlee-merchants])
                                  :entity->text #(str (:name %) " - " (:yodlee-id %))
                                  :type "typeahead-v3"
                                  :field [:yodlee-merchant]}])

            (field [:span "Description (" [:a {:href "https://regex101.com" :target "_new"} "regex tester"] ")" ]
                   [:input.input {:type "text"
                                  :field [:description]
                                  :spec ::entity/description}])

            [:div.field 
             [:p.help "Amount"]
             [:div.control 
              [:div.columns 
               [:div.column 
                (raw-field 
                 [:input.input {:type "number"
                                :placeholder ">="
                                :field [:amount-gte]
                                :spec ::entity/amount-gte
                                :step "0.01"}])]
               [:div.column
                (raw-field
                 [:input.input {:type "number"
                                :placeholder "<="
                                :field [:amount-lte]
                                :spec ::entity/amount-lte
                                :step "0.01"}])]]]]

            [:div.field 
             [:p.help "Day of Month"]
             [:div.control 
              [:div.columns 
               [:div.column 
                (raw-field 
                 [:input.input {:type "number"
                                :placeholder ">="
                                :field [:dom-gte]
                                :spec ::entity/dom-gte
                                :precision 0
                                :step "1"}])]
               [:div.column
                (raw-field
                 [:input.input {:type "number"
                                :placeholder "<="
                                :field [:dom-lte]
                                :spec ::entity/dom-lte
                                :precision 0
                                :step "1"}])]]]]

            

            [:h2.title.is-4 "Outcomes"]

            (field "Assign Vendor"
                   [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id])
                                  :entity-index @(re-frame/subscribe [::subs/all-vendors-index])
                                  :entity->text :name
                                  :type "typeahead-v3"
                                  :field [:vendor]
                                  :spec ::entity/vendor}])

            (with-meta 
              (field nil
                     [expense-accounts-field {:type "expense-accounts"
                                              :descriptor "account asssignment"
                                              :percentage-only? true
                                              :client (:client data)
                                              :locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client-or-bank-account (:id (:client data)) (:id (:bank-account data))]))
                                              :max 100
                                              :field [:accounts]}])
              {:key (some-> data :vendor :id str)})

            (field "Approval Status"
                   [button-radio
                    {:type "button-radio"
                     :field [:transaction-approval-status]
                     :options [[:unapproved "Unapproved"]
                               [:requires-feedback "Client Review"]
                               [:approved "Approved"]
                               [:excluded "Excluded from Ledger"]]}])

            (field "Note"
                   [:input.input {:type "text"
                                  :field [:note]
                                  :placeholder default-note
                                  :spec (s/nilable ::entity/note)}])

            [:div.is-divider]
            (error-notification)
            [:div.columns
             [:div.column
              [:a.button.is-medium.is-fullwidth.is-outlined {:on-click (dispatch-event [::test-clicked])
                                                             :disabled (status/disabled-for test-state)
                                                             :class (status/class-for test-state)}
               "Test Rule"]]
             [:div.column
              (submit-button "Save")]]]))])
