(ns auto-ap.views.pages.company.plaid
  (:require
   [auto-ap.effects.forward :as forward]
   [auto-ap.status :as status]
   [auto-ap.subs :as subs]
   [auto-ap.views.components.layouts :refer [side-bar-layout]]
   [auto-ap.views.pages.admin.plaid.table :as table]
   [auto-ap.shared-views.company.sidebar :refer [company-side-bar]]
   [auto-ap.views.pages.data-page :as data-page]
   [auto-ap.views.utils :refer [dispatch-event with-user]]
   [clojure.set :as set]
   [re-frame.core :as re-frame]
   [react-plaid-link :refer [usePlaidLink]]
   [reagent.core :as reagent]
   [vimsical.re-frame.fx.track :as track]))

(re-frame/reg-sub
 ::link-token
 (fn [db]
   (-> db ::link-token)))

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

(re-frame/reg-sub
 ::params
 :<- [::table/params]
 (fn [table-params]
   table-params))

(re-frame/reg-event-fx
 ::data-requested
 (fn [{:keys [db]} [_ params]]
   {:graphql {:token (:user db)
              :owns-state {:single ::page}
              :query-obj {:venia/queries [{:query/data [:plaid-item-page {:client-id (:id @(re-frame/subscribe [::subs/client]))
                                                                          :sort (:sort params)
                                                                          :start (:start params 0)}
                                                        [[:plaid-items [:id :last-updated :status
                                                                        [:client [:id]]
                                                                        [:accounts [:id :name :number :balance]]]]
                                                         :count
                                                         :start
                                                         :end
                                                         :total]]
                                           :query/alias :result}]
                          }
              :on-success (fn [result]
                            [::data-page/received ::page
                                 (set/rename-keys (:result result)
                                                  {:plaid-items :data})])}}))

(re-frame/reg-event-fx
 ::mounted
 (fn [{:keys [db]} _]
   {:dispatch [::data-requested]
    ::forward/register {:id ::plaid-item-deleted
                        :events #{::table/plaid-item-deleted}
                        :event-fn (fn [[_ _]]
                                        [::data-requested])}
    ::track/register {:id ::params
                      :subscription [::data-page/params ::page]
                      :event-fn (fn [params]
                                  [::data-requested params])}
    :db (dissoc db
                ::link-token
                ::message)}))

(re-frame/reg-event-fx
 ::unmounted
 (fn [_ _]
   {::forward/dispose {:id ::plaid-item-deleted}
    ::track/dispose [{:id ::params}]}))


(re-frame/reg-event-fx
 ::get-link-token
 [with-user]
 (fn [{:keys [db]} [_ _]]
   {:graphql {:token (:user db)
              :owns-state {:single ::get-link-token}
              :query-obj {:venia/queries [[:plaid-link-token {:client-id (:id @(re-frame/subscribe [::subs/client]))}
                                           [:token]]]} 
              :on-success [::authenticated]}}))

(re-frame/reg-event-fx
 ::plaid-linked
 (fn [{:keys [db]} [_ m]]
   {:db (assoc db ::message (:message (:link-plaid m)))
    :dispatch [::data-requested]}))

(re-frame/reg-event-fx
 ::exchange-token
 [with-user]
 (fn [{:keys [db]} [_ client public-token]]
   {:graphql {:token (:user db)
              :owns-state {:single ::get-link-token}
              :query-obj
              {:venia/operation {:operation/type :mutation
                                 :operation/name "LinkPlaid"}
               :venia/queries [{:query/data
                                [:link-plaid
                                 {:client-code client
                                  :public-token public-token}
                                 [:message]]}]}
              :on-success [::plaid-linked]}}))

(re-frame/reg-event-db
 ::authenticated
 (fn [db [_ link-token]]
   (-> db
       (assoc-in [::link-token] (:token (:plaid-link-token link-token))))))


(defn plaid-item-table []
  [table/table {:data-page ::page
                :status @(re-frame/subscribe [::status/single ::page])}])

(defn link-button [{:keys [link-token client-code]}]
  (let [plaid (usePlaidLink #js {:token link-token
                                 :onSuccess (fn [x]
                                              (re-frame/dispatch [::exchange-token client-code x]))})]
    [:div
     [:button.button.is-primary {:on-click (.-open plaid)}
      [:span [:span.icon [:i.fa.fa-external-link]] " Go to plaid"]]]))

(defn plaid-link-token-button []
  (let [status @(re-frame/subscribe [::status/single ::get-link-token])
        client @(re-frame/subscribe [::subs/client])]
    [:button.button.is-primary {:disabled (status/disabled-for status)
                                :class (status/class-for status)
                                :on-click (dispatch-event [::get-link-token (:code client)])}
     "Authenticate with Plaid (" (:name client) ")"]))

(defn link-flow []
  [:div
   (let [link-token @(re-frame/subscribe [::link-token])
         client-code (:code @(re-frame/subscribe [::subs/client]))]
     (cond
       (and link-token client-code)
       [:div
        "Authentication successful!"
        [:f> link-button {:link-token link-token
                          :client-code client-code}]]

       client-code
       [plaid-link-token-button]


       :else
       nil))])


(defn admin-plaid-item-content []
  (let [message @(re-frame/subscribe [::message])]
    [:div
     [:h1.title "Plaid Accounts"]
     (when message
       [:div.notification.is-info.is-light
        message])
     [plaid-item-table]
     [link-flow]
     ]))


(defn plaid-page []
  (reagent/create-class
   {:component-will-unmount #(re-frame/dispatch [::unmounted])
    :component-did-mount #(re-frame/dispatch [::mounted])
    :reagent-render (fn []
                      [side-bar-layout {:side-bar [company-side-bar {}]
                                        :main [admin-plaid-item-content]}])}))


