(ns auto-ap.events
  (:require
   [auto-ap.db :as db]
   [auto-ap.routes :as routes]
   [auto-ap.utils :refer [by]]
   [auto-ap.views.pages.data-page :as data-page]
   [auto-ap.views.utils :refer [parse-jwt with-user]]
   [bidi.bidi :as bidi]
   [clojure.string :as str]
   [goog.crypt.base64 :as b64]
   [re-frame.core :as re-frame]
   [auto-ap.ssr-routes :as ssr-routes]
   [cemerick.url :as url]))

(defn jwt->data [token]
  (js->clj (.parse js/JSON (b64/decodeString (second (str/split token #"\." ))))))


(defn client-query [token]
  (cond-> [:id :name :signature-file :code :email :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits :locations :locked-until :square-auth-token :feature-flags
           [:square-integration-status [:last-updated :last-attempt :message :state :id]]
           [:square-locations [:square-id :id :name :client-location]]
           [:ezcater-locations [:id [:caterer [:name :id]] :location]]
           [:emails [:id :email :description]]
           [:location-matches [:id :location :match]]
           [:bank-accounts [:id :start-date :numeric-code :code :number :bank-name :bank-code :check-number :name :routing :type :sort-order :visible :yodlee-account-id
                            [:integration-status [:last-updated :last-attempt :message :state :id]]
                            [:yodlee-account [:name :id :number]]
                            [:plaid-account [:name :id :number]]
                            [:intuit-bank-account [:name :id :external-id]]
                            :use-date-instead-of-post-date
                            :locations :include-in-reports :current-balance :yodlee-balance-old] ]
           [:address [:id :street1 :street2 :city :state :zip]]
           [:forecasted-transactions [:id :amount :identifier :day-of-month]]]
    (= "admin" (or (get (jwt->data token) "role") (get (jwt->data token) "user/role")) ) (into [[:yodlee-provider-accounts [:id [:accounts [:id :name :number :available-balance]]]]
                                                                                                [:plaid-items [:id [:accounts [:id :name :number :balance]]]]])))

(re-frame/reg-event-fx
 ::initialize-db
 (fn  [{:keys [_]} [_ token]]
   (let [handler (:handler (bidi/match-route routes/routes (.. js/window -location -pathname)))]
     (cond
       (= :login handler)
       {:db (assoc db/default-db
                   :active-route :login
                   :last-client-id (.getItem js/localStorage "last-client-id")
                   :user nil)}

       (and (not= :login handler) (not token))
       {:redirect "/login"
        :db (assoc db/default-db
                   :active-route :login
                   :last-client-id (.getItem js/localStorage "last-client-id")
                   :user token)}

       (and token (= "none" (or (get (jwt->data token) "role") (get (jwt->data token) "user/role")) ))
       {:redirect "/needs-activation"
        :db (assoc db/default-db
                   :active-route :needs-activation
                   :last-client-id (.getItem js/localStorage "last-client-id")
                   :user token)}

       :else
       {:db (assoc db/default-db
                   :active-route handler
                   :is-initial-loading? true
                   :last-client-id (.getItem js/localStorage "last-client-id")
                   :query-params (auto-ap.views.utils/query-params)
                   :user token)
        :graphql {:token token
                  :query-obj {:venia/queries [[:client (client-query token)]]}
                  :on-success [::received-initial]
                  :on-error [::failed-initial]}}))))


(re-frame/reg-event-db
 ::toggle-menu
 (fn  [db [_ which]]
   (update-in db [:menu which :active?] #(not %))))


(re-frame/reg-event-fx
 ::received-initial
 (fn  [{:keys [db]} [_ {clients :client}]]
   {:db (-> db 
            (assoc :clients (by :id clients) )
            (assoc :is-initial-loading? false)
            (assoc :client (or (when (= 1 (count clients)) (->> clients first :id ))
                               (->> clients
                                    (map :id)
                                    (filter #(= % (:last-client-id db)))
                                    first))))
    :interval {:action :start
               :id :refresh-clients
               :frequency 600000
               :event [::refresh-clients]}}))

(re-frame/reg-event-fx
 ::refresh-clients
 (fn [{:keys [db]}]
   (let [token (-> db :user)]
     {:graphql {:token token
                :query-obj {:venia/queries [[:client (client-query token)]]}
                :on-success [::received-refreshed-clients]}})))

(re-frame/reg-event-fx
 ::received-refreshed-clients
 (fn [{:keys [db]} [_ {clients :client}]]
   {:db (assoc db :clients (by :id clients))}))

(re-frame/reg-event-db
 ::failed-initial
 (fn  [db [_ e]]
   (assoc db :initial-error e

          :is-initial-loading? false
          :active-route :initial-error)))

(re-frame/reg-event-fx
 ::swap-client
 [with-user]
 (fn  [{:keys [db user]} [_ client]]
   (.setItem js/localStorage "last-client-id" (:id client))
   {:db (assoc db :client (:id client))
    :http {:token user
           :method :put
           :uri (str (bidi/path-for ssr-routes/only-routes
                                    :active-client
                                    :request-method :put)
                     "?"
                     (url/map->query {:search-client (:id client)}))}}))

(re-frame/reg-event-fx
 ::set-active-route
 (fn  [{:keys [db]} [_ handler params route-params]]
   (cond 
     (and (not= :login handler) (not (:user db)))
     {:redirect (bidi/path-for routes/routes :login)
      :db       (assoc db :active-route :login
                       :active-page :login
                       :menu nil
                       :page-failure nil)}

     (and (not= "admin" (:user/role (parse-jwt (:user db))))
          (str/includes? (name handler) "admin"))
     {:redirect (bidi/path-for routes/routes :index)
      :db       (assoc db :active-route :index
                       :active-page :index
                       :menu nil
                       :page-failure nil)}
     :else
     {:db (-> db
              (assoc :active-route handler
                     :page-failure nil
                     :menu nil
                     :query-params params
                     :route-params route-params)
              (data-page/dispose-all))})))


(re-frame/reg-event-fx
 ::logout
 (fn  [{:keys [db]} [_ logout-reason]]
   {:db (assoc db :user nil :menu {:client {:active? false}
                                   :account {:active? false}}
               :logout-reason logout-reason)
    :redirect (bidi/path-for routes/routes :login)
    :set-local-storage ["jwt" nil]}))


(re-frame/reg-event-db
 ::yodlee-merchants-received
 (fn [db [_ data]]
   (assoc db :yodlee-merchants (:yodlee-merchants data))))

(re-frame/reg-event-fx
 ::yodlee-merchants-needed
 (fn [{:keys [db]} _]
   {:graphql {:token (:user db)
              :query-obj {:venia/queries [[:yodlee-merchants
                                           [:name :yodlee-id :id]]]} 
              :on-success [::yodlee-merchants-received]}}))

(re-frame/reg-event-fx
  ::vendor-preferences-requested
  [with-user]
  (fn [{:keys [user]} [_ {:keys [ client-id vendor-id on-success on-failure owns-state]}]]
    {:graphql {:token      user
               :query-obj  {:venia/queries [[:vendor-by-id
                                             {:id vendor-id}
                                             [[:automatically-paid-when-due [:id]]
                                              [:schedule-payment-dom [[:client [:id]] :dom]]
                                              [:default-account [:id]]]]
                                            [:account-for-vendor
                                             {:vendor-id vendor-id
                                              :client-id client-id}
                                             [:name :id :numeric-code :location]]]}
               :owns-state owns-state
               :on-success (fn [r]
                             (let [schedule-payment-dom        (->> r
                                                             :vendor-by-id
                                                             :schedule-payment-dom
                                                             (filter (fn [spd]
                                                                       (= (-> spd :client :id)
                                                                          client-id)))
                                                             first
                                                             :dom)
                                   automatically-paid-when-due (boolean ((->> r :vendor-by-id :automatically-paid-when-due (map :id) set) client-id))]
                               (conj on-success  {:default-account             (:account-for-vendor r)
                                                  :schedule-payment-dom        schedule-payment-dom
                                                  :automatically-paid-when-due automatically-paid-when-due
                                                  :vendor-autopay?             (or automatically-paid-when-due (boolean schedule-payment-dom))})))
               :on-failure on-failure}}))
