(ns auto-ap.ssr.company.plaid
  (:require
   [auto-ap.datomic
    :refer [add-sorter-fields apply-pagination apply-sort-3 conn merge-query
            pull-attr pull-many-by-id query2]]
   [auto-ap.graphql.utils :refer [assert-can-see-client]]
   [auto-ap.logging :as alog]
   [auto-ap.plaid.core :as p]
   [auto-ap.query-params :refer [wrap-copy-qp-pqp]]
   [auto-ap.routes.utils :refer [wrap-client-redirect-unauthenticated
                                 wrap-secure]]
   [auto-ap.ssr-routes :as ssr-routes]
   [auto-ap.ssr.components :as com]
   [auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
   [auto-ap.ssr.svg :as svg]
   [auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers
                              default-grid-fields-schema html-response
                              wrap-merge-prior-hx wrap-schema-enforce]]
   [auto-ap.time :as atime]
   [bidi.bidi :as bidi]
   [clj-time.coerce :as coerce]
   [clj-time.core :as time]
   [clojure.string :as str]
   [datomic.api :as dc]
   [hiccup2.core :as hiccup]
   [malli.core :as mc]))
(def query-schema (mc/schema
                   [:maybe 
                           (into [:map {} ]
                                 default-grid-fields-schema)]))

(def default-read '[:db/id
                    :plaid-item/external-id
                    :plaid-item/access-token
                    :plaid-item/last-updated
                    :plaid-item/status

                    {:plaid-item/accounts [:db/id
                                           {:bank-account/_plaid-account [{:bank-account/integration-status
                                                                           [{ [ :integration-status/state :xform iol-ion.query/ident] [:db/ident]}
                                                                            :integration-status/message
                                                                            :integration-status/last-attempt
                                                                            :integration-status/last-updated]}]}
                                           :plaid-account/external-id
                                           :plaid-account/number
                                           :plaid-account/balance
                                           :plaid-account/name]}])

(defn fetch-ids [db request]
  (let [query-params (:query-params request)
        query (cond-> {:query {:find []
                               :in ['$ '[?xx ...]]
                               :where ['[?e :plaid-item/client ?xx]]}
                       :args [db (:trimmed-clients request)]}

                (:sort query-params) (add-sorter-fields {"external-id" ['[?e :plaid-item/external-id ?sort-external-id]]
                                                         "plaid-bank-status" ['[?e :plaid-item/status ?sort-plaid-bank-status]]
                                                         "status" ['[?e :plaid-item/status ?sort-status]]}
                                                        query-params)

                true
                (merge-query {:query {:find ['?e]
                                      :where ['[?e :plaid-item/external-id]]}}))]

    (cond->> (query2 query)
      true (apply-sort-3 query-params)
      true (apply-pagination query-params))))


(defn hydrate-results [ids db _]
  (let [results (pull-many-by-id db default-read ids)]
    (->> ids
         (map results))))

(defn fetch-page [request]
  (let [db                                           (dc/db conn)
        {ids-to-retrieve :ids matching-count :count} (fetch-ids db request)]
    [(hydrate-results ids-to-retrieve db request)
     matching-count]))



(defn plaid-link-script [token]
  (format "window.plaid = Plaid.create(
           { token: \"%s\",
             onSuccess: function (x) { htmx.trigger(\"#link-account\", \"linked\", {\"public_token\": x})}
             })", token))


(defn link [{{client-code "client_code" public-token "public_token"} :form-params
             :keys [identity]
             :as request}]
  (alog/info ::linking
             :request request)
  (when-not client-code
    (throw (ex-info "Client not provided" {:validation-error "Client not provided."})))
  (when-not public-token
    (throw (ex-info "Public token not provided" {:validation-error "public token not provided"})))
  (alog/info ::linking-plaid :id identity :client-code client-code)
  (assert-can-see-client identity (pull-attr (dc/db conn) :db/id [:client/code client-code]))
  (let [access-token (:access_token (p/exchange-public-token public-token client-code))
        account-result (p/get-accounts access-token )
        item {:plaid-item/client [:client/code client-code]
              :plaid-item/external-id (-> account-result :item :item_id )
              :plaid-item/access-token access-token
              :plaid-item/status (or (some-> account-result :item :error)
                                        "SUCCESS")
              :plaid-item/last-updated (coerce/to-date (time/now))
              :db/id "plaid-item"}]

    @(dc/transact conn (->> (:accounts account-result)
                           (map (fn [a]
                                  (let [balance (some-> a :balances :current (* 0.01))]
                                    (cond-> {:plaid-account/external-id (:account_id a)
                                             :plaid-account/number (:mask a)
                                             :plaid-account/name (str (:name a) " " (:mask a))
                                             :plaid-item/_accounts "plaid-item"}
                                      balance (assoc :plaid-account/balance balance)))))
                           (into [item])))
    (alog/info ::access-token-was :token access-token)
    {:headers {"Hx-redirect" (bidi/path-for ssr-routes/only-routes
                                            :company-plaid)}}))

(defn relink [{{:keys [plaid-item-id] :as qp} :query-params :keys [identity]}]
  (alog/info ::plaid-params :query-params qp)

  (let [pi (dc/pull (dc/db conn)
                    [:plaid-item/access-token {:plaid-item/client [:client/code]}]
                    (Long/parseLong plaid-item-id))]
    (assert-can-see-client identity (pull-attr (dc/db conn) :db/id [:client/code (-> pi :plaid-item/client
                                                                                     :client/code)]))
    (html-response
     [:div
      [:script (hiccup/raw (plaid-link-script (p/get-relink-token (-> pi :plaid-item/client
                                                                      :client/code)
                                                                  (-> pi :plaid-item/access-token))))]
      [:script (hiccup/raw "window.plaid.open()")]
      #_(com/button {:color   :primary
                     :id      "link-account"
                     :onClick "window.plaid.open()"}
                    (com/button-icon {} svg/refresh)
                    "Start relink")])))


(def grid-page 
  (helper/build 
    {:id          "plaid-table"
     :nav         com/company-aside-nav
     :fetch-page  fetch-page
     :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
                                             :company)}
                    "My Company"]

                   [:a {:href (bidi/path-for ssr-routes/only-routes
                                             :company-plaid)}
                    "Plaid"]]
     :title          "Plaid Accounts"
     :entity-name    "Plaid accounts"
                 :query-schema query-schema
     :route          :company-plaid-table
     :action-buttons (fn [request]
                       (when-let [client-code (:client/code (:client request))]
                         [[:div {:hx-post    (str (bidi/path-for ssr-routes/only-routes
                                                                 :company-plaid-link
                                                                 :request-method :post))
                                 :hx-vals    (hiccup/raw (format "js:{client_code: \"%s\", public_token: event.detail.public_token}", client-code))
                                 :hx-trigger "linked"}
                           [:script (hiccup/raw (plaid-link-script (p/get-link-token client-code)))]
                           (com/button {:color   :primary
                                        :id      "link-account"
                                        :onClick "window.plaid.open()"}
                                       (com/button-icon {} svg/refresh)
                                       (format "Link %s account" client-code))]]))
     :row-buttons    (fn [request e]
                       [[:div (com/button {:hx-put    (str (bidi/path-for ssr-routes/only-routes
                                                                          :company-plaid-relink)
                                                           "?plaid-item-id=" (:db/id e))
                                           :color     :primary
                                           :hx-target "closest div"}
                                          "Reauthenticate")]])
     :headers        [{:key      "plaid-item"
                       :name     "Plaid Item"
                       :sort-key "external-id"
                       :render   :plaid-item/external-id}
                      {:key      "integreat-plaid-status"
                       :name     "Integreat ↔ Plaid status"
                       :render   (fn [e]
                                   
                                   (let [bad-integration (->> (:plaid-item/accounts e)
                                                          (map (comp 
                                                                 first
                                                                :bank-account/_plaid-account))
                                                          (filter (comp #{:integration-state/failed :integration-state/unauthorized}
                                                                        :integration-status/state
                                                                        :bank-account/integration-status))
                                                          first
                                                                 :bank-account/integration-status)]
                                     [:div
                                      
                                      [:div.cursor-pointer (com/pill (cond-> {:color :primary} 
                                                                         bad-integration (assoc :color :red
                                                                                                :x-tooltip "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}"))

                                                      [:div.inline-flex.gap-2
                                                       (or 
                                                        (some-> bad-integration
                                                                :integration-status/state
                                                                name
                                                                str/capitalize)
                                                        "Success")
                                                       (when bad-integration 
                                                         " (detail)")
                                                        
                                                       
                                                       (when bad-integration 
                                                         [:template {:x-ref "tooltip"}
                                                          [:div.text-red-700
                                                           (:integration-status/message bad-integration)]])])]
                                      [:div.grid.grid-cols-2.gap-1.auto-cols-min.grid-flow-row.shrink
                                       [:div "Attempted: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-attempt e)) atime/normal-date)]
                                       [:div "Last Updated: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-updated e)) atime/normal-date)]]]))}
                      {:key      "plaid-bank-status"
                       :name     "Plaid ↔ Bank Status"
                       :sort-key "plaid-bank-status"
                       :render   (fn [e]
                                   (when-let [status (:plaid-item/status e)]
                                     [:div [:div (com/pill {:color :primary}
                                                           status)]
                                      [:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated e)) atime/normal-date)]]))}
                     
                      {:key           "accounts"
                       :name          "Accounts"
                       :show-starting "md"
                       :render        (fn [e]
                                        [:ul
                                         (for [a (:plaid-item/accounts e)]
                                           [:li [:svg.inline {:data-jdenticon-value (:db/id a) :width "24" :height "24"}] (:plaid-account/name a) " - " (:plaid-account/number a) " - updated "
                                            (atime/unparse-local (:plaid-account/last-synced a) atime/normal-date)])])}]}))


(def page (helper/page-route grid-page))
(def table (helper/table-route grid-page))

(def key->handler 
  (apply-middleware-to-all-handlers 
   {
    :company-plaid                                  page
    :company-plaid-table                            table
    :company-plaid-link                             link
    :company-plaid-relink                           relink
    
    }
   (fn [h]
     (-> h
         (wrap-copy-qp-pqp)
         (wrap-apply-sort grid-page)
         (wrap-merge-prior-hx)
         (wrap-schema-enforce :query-schema query-schema)
         (wrap-schema-enforce :hx-schema query-schema)
         (wrap-client-redirect-unauthenticated)
         (wrap-secure)))))