(ns auto-ap.views.pages.admin.rules.form
  (:require
   [auto-ap.events :as events]
   [auto-ap.forms :as forms]
   [auto-ap.forms.builder :as form-builder]
   [auto-ap.schema :as schema]
   [auto-ap.status :as status]
   [auto-ap.subs :as subs]
   [auto-ap.views.components :as com]
   [auto-ap.views.components.expense-accounts-field
    :as expense-accounts-field
    :refer [expense-accounts-field-v2]]
   [auto-ap.views.components.layouts :as layouts]
   [auto-ap.views.components.level :refer [left-stack]]
   [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 [coerce-float dispatch-event with-user]]
   [clojure.string :as str]
   [malli.core :as m]
   [re-frame.core :as re-frame]
   [reagent.core :as r]
   [vimsical.re-frame.cofx.inject :as inject]
   [vimsical.re-frame.fx.track :as track]))

;; 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
 ::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]}]}))


(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]]
   (-> 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 :amount-lte coerce-float)
                                       (update :amount-gte coerce-float)
                                       (update :accounts (fn [xs]
                                                           (mapv #(-> %
                                                                      (assoc  :amount-percentage (* (:percentage %) 100.0)))
                                                                 xs))))))))


(re-frame/reg-event-fx
  ::changed-vendor
  [(forms/in-form ::form)]
  (fn [{{{:keys [client]} :data} :db} [_ vendor]]
    (when (and (:id client) (:id vendor))
      {:dispatch [::events/vendor-preferences-requested {:client-id  (:id client)
                                                         :vendor-id  (:id vendor)
                                                         :on-success [::changed [:vendor-preferences]]}]})))

(re-frame/reg-event-fx
  ::changed-client
  [(forms/in-form ::form)]
  (fn [{{{:keys [vendor]} :data} :db} [_ client]]
    (when (and (:id client) (:id vendor))
      {:dispatch [::events/vendor-preferences-requested {:client-id  (:id client)
                                                         :vendor-id  (:id vendor)
                                                         :on-success [::changed [:vendor-preferences]]}]})))

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

                                 (= [:client] field)
                                 [[:bank-account] nil]
                                 :else
                                 []))))

(re-frame/reg-event-fx
 ::saving
 [with-user (forms/triggers-loading ::form) (forms/in-form ::form) (re-frame/inject-cofx ::inject/sub [::query])]
 (fn [{:keys [user] ::keys [query]} _]
   {:graphql
    {:token user
     :query-obj query
     :owns-state {:single ::form}
     :on-success (fn [result]
                   [::updated (:upsert-transaction-rule result)])}}))

(re-frame/reg-event-fx
 ::test-clicked
 [with-user (forms/triggers-loading ::form) (forms/in-form ::form) (re-frame/inject-cofx ::inject/sub [::test-query])]
 (fn [{:keys [user] ::keys [test-query]} _]
   {:graphql
    {:token user
     :owns-state {:single ::test}
     :query-obj 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]} _]
   {:db (forms/start-form db ::form {:client @(re-frame/subscribe [::subs/client])})}))

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

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



;; VIEWS


(re-frame/reg-event-fx
  ::mounted
  (fn [_ _]
    {::track/register [{:id           ::client
                        :subscription [::forms/field ::form [:client]]
                        :event-fn     (fn [c]
                                        [::changed-client c])}
                       {:id           ::vendor-change
                        :subscription [::forms/field ::form [:vendor]]
                        :event-fn     (fn [v]
                                        [::changed-vendor v])}]}))

(re-frame/reg-event-fx
  ::unmounted
  (fn []
    {::track/dispose [{:id ::client}
                      {:id ::vendor-change}]}))

(def rule-schema
  (m/schema [:map
             [:client {:optional true}
              [:maybe schema/reference]]
             [:bank-account {:optional true}
              [:maybe schema/reference]]
             [:description
              schema/not-empty-string]
             [:amount-gte {:optional true}
              [:maybe schema/money]]
             [:amount-lte {:optional true}
              [:maybe schema/money]]
             [:dom-gte {:optional true}
              [:maybe [:int {:min 1 :max 31}]]]
             [:dom-lte {:optional true}
              [:maybe [:int {:min 1 :max 31}]]]
             [:vendor {:optional true}
              [:maybe schema/reference]]
             [:transaction-approval-status {:optional true}
              [:maybe schema/approval-status]]
             [:note {:optional true}
              [:maybe :string]]]))

(defn form-contents []
  [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])}
   (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])
         default-note      @(re-frame/subscribe [::default-note])
         test-state        @(re-frame/subscribe [::status/single ::test])]
     [form-builder/builder {:change-event [::changed]
                            :submit-event [::saving ]
                            :id           ::form
                            :schema rule-schema}
      [form-builder/section {:title "Transaction Rule"}
       [form-builder/field-v2 {:required? true
                               :field     :client}
        "Client"
        [com/entity-typeahead {:entities     @(re-frame/subscribe [::subs/clients])
                       :auto-focus   true
                       :entity->text :name}]]

       
       [form-builder/field-v2 {:field [:bank-account]}
        "Bank account"
        [com/entity-typeahead {:entities     @(re-frame/subscribe [::subs/real-bank-accounts-for-client (:client data)])
                       :entity->text :name}]]

       [form-builder/field-v2 {:field :description
                               :required? true}
        [:span "Description (" [:a {:href "https://regex101.com" :target "_new"} "regex tester"] ")" ]
        [:input.input {:type  "text"}]]

       [:div.field 
        [:p.help "Amount"]
        [left-stack 
         [form-builder/raw-field-v2 {:field :amount-gte}
          [com/money-input {:placeholder ">="}]]
         "-"
         [form-builder/raw-field-v2 {:field :amount-lte}
          [com/money-input {:placeholder "<="}]]]]

       [:div.field 
        [:p.help "Day of month"]
        [left-stack 
         [form-builder/raw-field-v2 {:field :dom-gte}
          [com/number-input {:placeholder ">="
                             :style {:width "7em"}}]]
         "-"
         [form-builder/raw-field-v2 {:field :dom-lte}
          [com/number-input {:placeholder "<="
                             :style {:width "7em"}}]]]]
       

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

       [form-builder/field-v2 {:field :vendor}
        "Assign Vendor"
        [com/search-backed-typeahead {:search-query (fn [i]
                                                  [:search_vendor
                                                   {:query i}
                                                   [:name :id]])}]]

       [form-builder/field-v2 {:field :accounts}
        "Accounts"
        [expense-accounts-field-v2 {: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}]]

       [form-builder/field-v2 {:field :transaction-approval-status}
        "Approval Status"
        [com/button-radio-input
         {:options [[:unapproved "Unapproved"]
                    [:requires-feedback "Client Review"]
                    [:approved "Approved"]
                    [:excluded "Excluded from Ledger"]]}]]

       [form-builder/field-v2 {:field :note}
        "Note"
        [:input.input {:type        "text"
                       :placeholder default-note}]]

       [:div.is-divider]
       [form-builder/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
         [form-builder/submit-button {:class ["is-fullwidth"]}
          "Save"]]]]])])

(defn form [_]
  (r/create-class
    {:display-name           "rule-form"
     :component-did-mount    #(re-frame/dispatch [::mounted])
     :component-will-unmount #(re-frame/dispatch [::unmounted])
     :reagent-render         (fn [p]
                               [form-contents p])}))
