(ns auto-ap.events
  (:require [re-frame.core :as re-frame]
            [auto-ap.db :as db]
            [auto-ap.subs :as subs]
            [auto-ap.routes :as routes]
            [auto-ap.effects :as effects]
            
            [auto-ap.utils :refer [by]]
            [venia.core :as v]
            [bidi.bidi :as bidi]
            [cemerick.url :refer [url]]
            [goog.crypt.base64 :as b64]
            [clojure.string :as str]))

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

(def vendor-query
  [:id :name :hidden :terms [:default-account [:name :id :location]]
   [:account-overrides [[:client [:id :name]] :id [:account [:id :numeric-code :name]]]]
   [:automatically-paid-when-due [:id :name]]
   [:terms-overrides [[:client [:id :name]] :id :terms]]
   [:schedule-payment-dom [[:client [:id :name]] :id :dom]]
   [:usage [:client-id :count]]
   [:primary-contact [:name :phone :email :id]]
   [:secondary-contact [:id :name :phone :email]]
   :print-as :invoice-reminder-schedule :code
   :legal-entity-first-name :legal-entity-middle-name :legal-entity-last-name
   :legal-entity-tin :legal-entity-tin-type
   :legal-entity-1099-type
   [:address [:street1 :street2 :city :state :zip]]])

(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
           [: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
                            [:yodlee-account [:name :id :number]]
                            [:intuit-bank-account [:name :id :external-id]]
                            :locations :include-in-reports :current-balance :yodlee-balance-old] ]
           [:address [: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")) ) (conj [:yodlee-provider-accounts [:id [:accounts [:id :name :number :available-balance]]]])))

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

       :else
       {:db (assoc db/default-db
                   :active-page 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)]
                                              [:vendor
                                               vendor-query]
                                              [:accounts [:numeric-code :location :name :type :account_set :applicability :id [:client-overrides [:name :id [:client [:name :id]]]]]]]}
                  :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
 ::logged-in
 (fn  [{:keys [db]} [_ token user]]
   {:graphql {:token token
              :query-obj {:venia/queries [[:client
                                           (client-query token)]
                                          [:vendor
                                           vendor-query]
                                          [:accounts [:numeric-code :name :location :type :account_set :applicability :id [:client-overrides [:name [:client [:name :id]]]]]]]}
              
              :on-success [::received-initial]
              :on-error [::failed-initial]}
    :db (assoc db
               :user (assoc user :token token)
               :is-initial-loading? true)}))

(re-frame/reg-event-fx
 ::received-initial
 (fn  [{:keys [db]} [_ {accounts :accounts clients :client vendors :vendor :as x}]]
   
   {:db (-> db 
            (assoc :clients (by :id clients) )
            (assoc :vendors (by :id vendors) )
            (assoc :is-initial-loading? false)
            (assoc :accounts accounts )
            (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-page :initial-error)))

(re-frame/reg-event-db
 ::swap-client
 (fn  [db [_ client]]
   (.setItem js/localStorage "last-client-id" (:id client))
   (assoc db :client (:id client))))

(re-frame/reg-event-db
 ::change-form
 (fn  [db [_ location field value]]
   (if value
     (assoc-in db (into location field) value)
     (update-in db (into location (butlast field)) dissoc (last field)))))


(re-frame/reg-event-fx
 ::set-active-page
 (fn  [{:keys [db]} [_ handler params]]
   

   (if (and (not= :login handler) (not (:user db)))
     {:redirect "/login"
      :db (assoc db :active-page :login
                 :page-failure nil
                 :auto-ap.forms/forms nil)}
     {:db (-> db
              (assoc :active-page handler
                     :page-failure nil
                     :query-params params
                     :auto-ap.forms/forms nil)
              (auto-ap.views.pages.data-page/dispose-all))})))

(re-frame/reg-event-db
 ::imported-invoices
 (fn  [db [_ new-invoices]]
   (assoc-in db [:invoices :pending] new-invoices)))

(re-frame/reg-event-fx
 ::view-pending-invoices
 (fn [cofx []]
   {:db (assoc-in (:db cofx) [:status :loading] true)
    :graphql {:token (-> cofx :db :user)
              :query-obj {:venia/queries [[:invoice
                                           {:imported false :client_id (:id @(re-frame/subscribe [::subs/client]))}
                                           [:id :total :invoice-number :date [:vendor [:name :id]] [:client [:name :id]]]]]}
              
              :on-success [::received-invoices :pending]}}))

(re-frame/reg-event-fx
 ::view-unpaid-invoices
 (fn [cofx []]
   {:db (assoc-in (:db cofx) [:status :loading] true)
    :graphql {:token (-> cofx :db :user)
              :query-obj {:venia/queries [[:invoice
                                           {:imported true :client_id (:id @(re-frame/subscribe [::subs/client]))}
                                           [:id :total :invoice-number :date [:vendor [:name :id]] [:client [:name :id]]]]]} 
              :on-success [::received-invoices :unpaid]}}))

(re-frame/reg-event-db
 ::submitted-new-invoice
 (fn  [db [_ invoice]]
   (assoc db :new-invoice {})))

(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-fx
 ::submit-new-invoice
 (fn  [{:keys [db]} [_ invoice]]
   {:http {:method :post
           :token (-> db :user)
           :uri "/api/invoices"
           :body (pr-str {:rows [(assoc invoice :imported true)]})
           :headers {"Content-Type" "application/edn"}
           :on-success [::submitted-new-invoice]}
    :db (assoc-in db [:new-invoice :loading?] true)}))

(re-frame/reg-event-db
 ::received-invoices
 (fn  [db [_ type result]]
   (let [new-invoices (if (:invoice result)
                        (:invoice result)
                        result)]
     (-> db
         (assoc-in [:invoices type]  new-invoices)
         (assoc-in [:status :loading] false)))))

(re-frame/reg-event-db
 ::change-form-state
 (fn  [db [_ target value]]
   (assoc-in db target value)))

(re-frame/reg-event-db
 ::change-nested-form-state
 (fn  [db [_ form more value]]
   (update-in db form
              (fn [x]
                (assoc-in x more value)))))



(re-frame/reg-event-db
 ::page-failed
 (fn [db [_ result]]
   (println "Page failure" result)
   (assoc db :page-failure result
          :status 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]}}))
