(ns auto-ap.views.pages.admin.yodlee
  (:require [re-frame.core :as re-frame]
            [auto-ap.forms :as forms]
            [reagent.core :as reagent]
            [clojure.string :as str]
            [cljs-time.format :as f]
            [cljs-time.core :as time]
            [auto-ap.subs :as subs]
            [auto-ap.events :as events]
            [auto-ap.entities.clients :as entity]
            [auto-ap.views.components.layouts :refer [side-bar-layout]]
            [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
            [auto-ap.views.components.address :refer [address-field]]
            [auto-ap.views.utils :refer [login-url dispatch-event dispatch-value-change bind-field horizontal-field str->date date->str with-user]]
            [auto-ap.views.components.modal :as modal]
            [auto-ap.status :as status]
            [cljs.reader :as edn]
            [auto-ap.routes :as routes]
            [bidi.bidi :as bidi]))



(re-frame/reg-sub
 ::authentication
 (fn [db]
   (-> db ::yodlee :authentication)))

(re-frame/reg-sub
 ::can-submit
 (fn [db]
   true))

(re-frame/reg-sub
 ::loading?
 (fn [db]
   (-> db ::yodlee :loading?)))

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

(re-frame/reg-sub
 ::accounts-loading?
 (fn [db]
   (-> db ::yodlee :accounts-loading?)))

(re-frame/reg-sub
 ::provider-accounts-loading?
 (fn [db]
   (-> db ::provider-accounts-loading?)))

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

(re-frame/reg-event-fx
 ::authenticate-with-yodlee
 (fn [{:keys [db]} _]
   {:db (assoc-in db [::yodlee :loading?] true)
    :http {:token (:user db)
           :method :get 
           :headers {"Content-Type" "application/edn"}
           :uri (str "/api/yodlee/fastlink")
           :on-success [::authenticated]
           :on-error [::save-error]}}))

(re-frame/reg-event-fx
 ::mounted
 (fn [{:keys [db]} _]
   {:db (-> db
            (assoc ::yodlee {:provider-accounts-loading? true})
            (assoc ::save-error nil)
            (assoc ::provider-accounts [])
            (assoc ::provider-accounts-loading? true))
    :http {:token (:user db)
           :method :get 
           :headers {"Content-Type" "application/edn"}
           :uri (str "/api/yodlee/provider-accounts")
           :on-success [::got-provider-accounts]
           :on-error [::save-error]}}))

(re-frame/reg-event-fx
 ::kicked
 (fn [{:keys [db]} [_ id state]]
   {:dispatch [::mounted]}))

(re-frame/reg-event-fx
 ::kicked
 (fn [{:keys [db]} [_ id state]]
   {:dispatch [::mounted]}))

(re-frame/reg-event-fx
 ::kick
 (fn [{:keys [db]} [_ id]]
   {:http {:token (:user db)
           :method :post 
           :headers {"Content-Type" "application/edn"}
           :uri (str "/api/yodlee/provider-accounts/" id)
           :on-success [::kicked id :kicked]
           :on-error [::kicked id :errored]}}))

(re-frame/reg-event-fx
 ::got-accounts
 (fn [{:keys [db]} [_ accounts]]
   {:db (-> db
            (assoc-in [::yodlee :accounts] accounts)
            (assoc-in [::yodlee :accounts-loading?] false))}))

(re-frame/reg-event-fx
 ::got-provider-accounts
 (fn [{:keys [db]} [_ accounts]]
   {:db (-> db
            (assoc-in [::provider-accounts] accounts)
            (assoc-in [::provider-accounts-loading?] false))}))

(re-frame/reg-event-fx
 ::authenticated
 (fn [{:keys [db]} [_ authentication]]
   {:db (-> db
            (assoc-in [::yodlee :authentication] authentication)
            (assoc-in [::yodlee :loading?] false))}))

(re-frame/reg-event-fx
 ::authenticated-mfa
 (fn [{:keys [db]} [_ provider-account-id authentication]]
   {:db (-> db
            (assoc-in [::yodlee :authentication] authentication)
            (assoc-in [::yodlee :loading?] false)
            (forms/stop-form [::mfa-form provider-account-id]))}))

(re-frame/reg-event-fx
 ::save-error
 (fn [{:keys [db]} [_ authentication]]
   {:db (assoc :db ::load-error "error")}))

(defn yodlee-link-button []
  [:div
   (let [authentication @(re-frame/subscribe [::authentication])
         loading? @(re-frame/subscribe [::loading?])]
     
     (if authentication
       [:div
        "Authentication successful!"
        [:form {:action (:url authentication) :method "POST"}
         [:input {:type "hidden"
                  :name "rsession"
                  :value (:session authentication)}]
         [:input {:type "hidden"
                  :name "token"
                  :value (:token authentication)}]
         [:input {:type "hidden"
                  :name "app"
                  :value (:app authentication)}]

         [:input {:type "hidden"
                  :name "redirectReq"
                  :value "true"}]
         [:button.button.is-primary [:span [:span.icon [:i.fa.fa-external-link]] " Go to yodlee"]]]]
       
       [:button.button.is-primary {:class (if loading? "is-loading" "") :on-click (dispatch-event [::authenticate-with-yodlee])} "Authenticate with Yodlee"]))])

(defn yodlee-date->date [d]
  (try
    (some-> d
            (str->date (:date-time-no-ms f/formatters))
            )
    (catch js/Error e
      nil)))

(defn yodlee-date->str [d]
  (try
    (or (some-> d
                (str->date (:date-time-no-ms f/formatters))
                date->str)
        "N/A")
    (catch js/Error e
      "N/A")))

(defn yodlee-accounts-table [accounts]
  (let [bank-accounts @(re-frame/subscribe [::bank-accounts-by-yodlee-account-id])]
    [:div
     [:table.table
      [:thead
       [:tr
        [:th "Account Name"]
        [:th "Account Number"]
        [:th "Yodlee Account Number"]
        [:th "Balance"]
        [:th "Yodlee Status"]
        [:th "Usage"]]]
      [:tbody 

       (for [account accounts]
         ^{:key (:id account)} [:tr
                                [:td (:accountName account)]
                                [:td (:accountNumber account)]
                                [:td (:id account)]
                                [:td.has-text-right (:amount (:balance account))]
                                [:td (str/join ", " (map :additionalStatus (:dataset account)))]
                                [:td
                                 (when-let [bank-accounts (get bank-accounts (:id account))]
                                   [:div.tags
                                    (for [bank-account bank-accounts]
                                      ^{:key (:id bank-account)}
                                      [:div.tag (:name bank-account) " (" (:code bank-account) ")"])])]
                                ])]]]))

(re-frame/reg-event-fx
 ::reauthenticate-mfa
 [with-user ]
 (fn [{:keys [user db]} [_ provider-account-id ]]
   {:db (forms/loading db [::mfa-form provider-account-id])
    :http {:token user
           :method :post 
           :headers {"Content-Type" "application/edn"}
           :uri (str "/api/yodlee/reauthenticate/" provider-account-id )
           :body {"loginForm"
                  {"row"
                   (->> (get-in db [::forms/forms [::mfa-form provider-account-id]])
                        :data
                        :login
                        (sort-by (fn [[k v]] k))
                        (map second)
                        (map (fn [row]
                               {"field"
                                (mapv (fn [[k v]]
                                        {"id" k
                                         "value" v})
                                      row)})))}
                  "field"
                  (mapv (fn [[k v]]
                          {"id" k
                           "value" v})
                        (:mfa (:data (get-in db [::forms/forms [::mfa-form provider-account-id]]))))}
           
           :on-success [::authenticated-mfa provider-account-id]
           :on-error [::forms/save-error [::mfa-form provider-account-id] ]}}))

(re-frame/reg-event-fx
 ::provider-account-refreshed
 (fn [{:keys [db]} [_ i result]]
   
   {:db (assoc-in db [::provider-accounts] result)
    :dispatch [::forms/form-closing [::refresh-provider-account i]]}))

(re-frame/reg-event-fx
 ::refresh-provider-account
 [with-user ]
 (fn [{:keys [user db]} [_ provider-account-id ]]
   {:db (forms/loading db [::refresh-provider-account provider-account-id])
    :http {:token user
           :method :post 
           :headers {"Content-Type" "application/edn"}
           :uri (str "/api/yodlee/provider-accounts/refresh/" provider-account-id )
           :body {}
           :on-success [::provider-account-refreshed provider-account-id]
           :on-error [::forms/save-error [::refresh-provider-account provider-account-id] ]}}))

(re-frame/reg-event-fx
 ::provider-account-deleted
 (fn [{:keys [db]} [_ i result]]
   {:db (assoc-in db [::provider-accounts] result)
    :dispatch-n [[::forms/form-closing [::refresh-provider-account i]]
                 [::modal/modal-closed ]]}))

(re-frame/reg-event-fx
 ::delete-provider-account
 [with-user ]
 (fn [{:keys [user db]} [_ provider-account-id ]]
   {:http {:token user
           :method :post 
           :owns-state {:single ::delete-provider-account}
           :headers {"Content-Type" "application/edn"}
           :uri (str "/api/yodlee/provider-accounts/delete/" provider-account-id )
           :body {}
           :on-success [::provider-account-deleted provider-account-id]
           :on-error [::forms/save-error [::delete-provider-account provider-account-id] ]}}))



(re-frame/reg-event-fx
 ::delete-requested
 [with-user]
 (fn [{:keys [user db]} [_ account-id]]
   {:dispatch 
    [::modal/modal-requested {:title "Delete Provider account "
                              :body [:div "Are you sure you want to delete provider account " account-id "?"]
                              :confirm {:value "Delete provider account"
                                        :status-from [::status/single ::delete-provider-account]
                                        :class "is-danger"
                                        :on-click (dispatch-event [::delete-provider-account account-id])
                                        :close-event [::status/completed ::delete-provider-account]}
                              :cancel? true}]}))


(defn delete-button [account-id]
  [:button.button
   {:on-click (dispatch-event [::delete-requested account-id])}
   [:span.icon [:i.fa.fa-times]]])

(re-frame/reg-sub
 ::bank-accounts-by-yodlee-account-id
 :<- [::subs/bank-accounts]
 (fn [bank-accounts]
   (group-by :yodlee-account-id bank-accounts)))

(defn yodlee-provider-accounts-table []
  (let [bank-accounts @(re-frame/subscribe [::bank-accounts-by-yodlee-account-id])]
    
    (if @(re-frame/subscribe [::provider-accounts-loading?])
      [:div "Loading..."]
      [:div.columns
       [:div.column.is-half
        (doall 
         (for [account @(re-frame/subscribe [::provider-accounts])
               :let [{:keys [error status] :as g} @(re-frame/subscribe [::forms/form [::refresh-provider-account (:id account)]])
                     total-usages (mapcat (comp bank-accounts :id) (:accounts account))]]
           
           ^{:key (:id account)}
           [:div.card {:style {:margin-bottom "1em"}}
            [:div.card-header
             [:div.card-header-title "Provider account " (:id account)]
             [:div.card-header-icon
              (when (seq total-usages)
                [:div.tags 
                 [:div.tag.is-primary (count total-usages) " usages"]])]
             [:div.card-header-icon
              [delete-button (:id account)]]
             [:div.card-header-icon
              (cond 
                (= :loading status) [:button.button.is-disabled.is-loading [:i.fa.fa-refresh]]
                error [:button.button.is-disabled [:span.icon [:i.fa.fa-exclamation-triangle]]]
                :else 
                [:button.button
                 {:on-click (dispatch-event [::refresh-provider-account (:id account)])}
                 [:span.icon [:i.fa.fa-refresh]]])]]
            [:div.card-content

             (if (> (some-> (-> account :dataset first :lastUpdated)
                            (yodlee-date->date )
                            (time/interval (time/now))
                            (time/in-days ))
                    1)
               [:div.notification.is-info.is-light
                [:div.level
                 [:div.level-left 
                  [:div.level-item 
                   [:p 
                    "This account was last updated on "
                    (yodlee-date->str (-> account :dataset first :lastUpdated))
                    ", and last attempted "
                    (yodlee-date->str (-> account :dataset first :lastUpdateAttempt))
                    "."]]]
                 [:div.level-right [:button.button.is-success {:on-click (dispatch-event [::kick (:id account)] )} "Sync yodlee with bank" ]]]

                ])
             

             [yodlee-accounts-table (:accounts account)]
             (if (not= (-> account :dataset first :additionalStatus)
                       "AVAILABLE_DATA_RETRIEVED")
               [:div 
                [:div.notification.is-info.is-warning
                 [:div.level
                  [:div.level-left 
                   [:div.level-item 
                    "This provider account's status is '"
                    (-> account :dataset first :additionalStatus)
                    "'. If this is in error, it might help to try reauthenticating by filling out the form below."]]]]
                (let [{error :error account-data :data } @(re-frame/subscribe [::forms/form [::mfa-form (:id account)]])
                      change-event [::forms/change [::mfa-form (:id account)]]
                      {:keys [form-inline field field-holder raw-field error-notification submit-button]} (forms/vertical-form {:can-submit [::can-submit]
                                                                                                                                :change-event change-event
                                                                                                                                :submit-event [::reauthenticate-mfa (:id account)]
                                                                                                                                :id [::mfa-form (:id account)]} )]
                  (form-inline {:title "Reauthenticate"}
                               [:<>
                                (error-notification)
                                (doall 
                                 (for [[row i] (map vector  (-> account :loginForm last :row) (range))
                                       f (:field row)
                                       :let [options (map :optionValue (:option f))]]
                                   ^{:key (:id f)}
                                   [:div 
                                    (field (:label row)
                                           [:input.input {:type "text" :field [:login i (:id f)]}])
                                    (if (seq options)
                                      [:ul
                                       (for [o options]
                                         ^{:key o}
                                         [:li [:pre o]])])]))
                                (doall 
                                 (for [f (-> account :field)]
                                   ^{:key (:id f)}
                                   (field (:label f)
                                          [:input.input {:type "text" :mfa [:form (:id f)] :value (-> f :field first :value)}])))
                                (submit-button "Reauthenticate")]))])]]))]])))


(defn admin-yodlee-content []
  [(with-meta
     (fn []
       [:div
        [:h1.title "Yodlee provider accounts"]
        
        [yodlee-provider-accounts-table]
        [yodlee-link-button]])
     {:component-did-mount (fn []
                             (re-frame/dispatch [::mounted]))})])

(defn admin-yodlee-page []
  [side-bar-layout {:side-bar [admin-side-bar {}]
                    :main [admin-yodlee-content]}])
