(ns auto-ap.datomic.clients
  (:require
   [auto-ap.datomic
    :refer [add-sorter-fields
            apply-pagination
            apply-sort-3
            conn
            merge-query
            pull-many
            query2]]
   [auto-ap.graphql.utils :refer [limited-clients]]
   [clj-time.coerce :as coerce]
   [clojure.string :as str]
   [com.brunobonacci.mulog :as mu]
   [datomic.api :as dc]
   [clj-http.client :as client]
   [auto-ap.solr :as solr]
   [clojure.set :as set]))

(def full-read '[*
                 {:client/square-integration-status [:integration-status/message
                                                     :integration-status/last-attempt
                                                     :integration-status/last-updated
                                                     :db/id
                                                     {:integration-status/state [:db/ident]}]}
                 {:client/address [*]}
                 {:client/square-locations [:square-location/square-id
                                            :square-location/name
                                            :square-location/client-location
                                            :db/id]}

                 {:client/ezcater-locations [{:ezcater-location/caterer [:ezcater-caterer/name :db/id]}
                                             :ezcater-location/location
                                             :db/id]}
                 {:client/bank-accounts [* {:bank-account/type                [*]
                                            :bank-account/yodlee-account      [:yodlee-account/name :yodlee-account/id :yodlee-account/number]
                                            :bank-account/plaid-account       [:plaid-account/name :db/id :plaid-account/number :plaid-account/balance]
                                            :bank-account/intuit-bank-account [:intuit-bank-account/name :intuit-bank-account/external-id :db/id]
                                            :bank-account/integration-status  [:integration-status/message
                                                                               :db/id
                                                                               :integration-status/last-attempt
                                                                               :integration-status/last-updated
                                                                               {:integration-status/state [:db/ident]}]}]}
                 {:yodlee-provider-account/_client [*]}
                 {:plaid-item/_client [*]}
                 {:client/emails [:db/id :email-contact/email :email-contact/description]}])

(defn cleanse [e]
  (-> e
      (assoc :client/yodlee-provider-accounts (get e :yodlee-provider-account/_client))
      (assoc :client/plaid-items (get e :plaid-item/_client))
      (update :client/locked-until #(some-> % coerce/to-date-time))
      (update-in [:client/square-integration-status :integration-status/state] :db/ident)
      (update-in [:client/square-integration-status :integration-status/last-attempt] #(some-> % coerce/to-date-time))
      (update-in [:client/square-integration-status :integration-status/last-updated] #(some-> % coerce/to-date-time))
      (update :client/location-matches
              (fn [lms]
                (map #(assoc % :location-match/match (first (:location-match/matches %))) lms)))
      (update :client/bank-accounts
              (fn [bas]
                (map (fn [i ba]
                       (-> ba
                           (update :bank-account/type :db/ident)
                           (update-in [:bank-account/integration-status :integration-status/state] :db/ident)
                           (update-in [:bank-account/integration-status :integration-status/last-attempt] #(some-> % coerce/to-date-time))
                           (update-in [:bank-account/integration-status :integration-status/last-updated] #(some-> % coerce/to-date-time))
                           (update :bank-account/start-date #(some-> % (coerce/to-date-time)))
                           (update :bank-account/sort-order (fn [so] (or so i)))))
                     (range) bas)))))
(defn get-all []
  (->> (dc/q '[:find (pull ?e r)
               :in $ r
               :where [?e :client/name]]
             (dc/db conn)
             full-read)
       (map first)
       (map cleanse)))

(defn get-minimal []
  (->> (dc/q '[:find (pull ?e [:client/name :client/code :client/locations :db/id
                               :client/groups
                               {:client/bank-accounts [{:bank-account/type [:db/ident]}
                                                       :bank-account/name
                                                       :bank-account/sort-order
                                                       :bank-account/bank-name
                                                       :bank-account/visible
                                                       :bank-account/current-balance
                                                       :bank-account/locations
                                                       :bank-account/code
                                                       :db/id]
                                :client/emails [:db/id
                                                :email-contact/email
                                                :email-contact/description]}])
               :where [?e :client/name]]
             (dc/db conn))
       (map first)
       (map cleanse)))

(defn get-by-id [id]
  (->>
   (dc/pull (dc/db conn)
            full-read
            id)
   (cleanse)))

(defn code->id [code]
  (->>
   (dc/q '[:find  ?e
           :in    $ ?code
           :where [?e :client/code ?code]]
         (dc/db conn) code)
   (first)
   (first)))

(defn best-match [identifier]
  (when (and identifier (not-empty identifier))
    (some-> (solr/query solr/impl "clients"
                        {"query"  (format "_text_:\"%s\"" (str/upper-case (solr/escape identifier)))
                         "fields" "id"})
            first
            :id
            Long/parseLong
            (#(hash-map :db/id %)))))



(defn exact-match [identifier]
  (when (and identifier (not-empty identifier))
    (some-> (solr/query solr/impl "clients"
                        {"query"  (format "exact:\"%s\"" (str/upper-case (solr/escape identifier)))
                         "fields" "id"})
            first
            :id
            Long/parseLong
            (#(hash-map :db/id %)))))

(defn rebuild-search-index []
  (solr/index-documents-raw solr/impl
                            "clients"
                            (for [result (map first (dc/q '[:find (pull ?v [:client/name :client/matches :db/id :client/code])
                                                            :in $
                                                            :where [?v :client/code]]
                                                          (dc/db conn)))
                                  :let   [matches (conj (or (:client/matches result) [])
                                                        (:client/name result))]]
                              {"id"    (:db/id result)
                               "name"  matches
                               "code"  (:client/code result)
                               "exact" (map str/upper-case matches)})))

(defn raw-graphql-ids [db args]
  (let [name-like-ids (cond (not (str/blank? (:name-like args)))
                            (set (map (comp #(Long/parseLong %) :id)
                                      (solr/query solr/impl "clients"
                                                  {"query" (format "_text_:(%s*)" (str/upper-case (solr/escape (:name-like args))))
                                                   "fields" "id"
                                                   "limit" 300})))

                            (not (str/blank? (:code args)))
                            (set (map (comp #(Long/parseLong %) :id)
                                      (solr/query solr/impl "clients" {"query" (format "_text_:(%s*)" (str/upper-case (solr/escape (:code args))))
                                                                       "fields" "id"
                                                                       "limit" 300})))

                            :else
                            nil)
        matching-ids name-like-ids
        valid-ids (if matching-ids
                    (set/intersection (set (map :db/id (:clients args)))
                                      matching-ids)
                    (set (map :db/id (:clients args))))


        query         (cond-> {:query {:find []
                                       :in ['$]
                                       :where []}
                               :args [db]}
                        valid-ids
                        (merge-query {:query {:in    ['[?e ...]]}
                                      :args  [(set valid-ids)]})


                        (:sort args) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]}
                                                        args)

                        true
                        (merge-query {:query {:find ['?sort-default '?e] :where ['[?e :client/name ?sort-default]]}}))]
    (->> (query2 query)
         (apply-sort-3 (update args :sort conj {:sort-key "default-2" :asc true}))
         (apply-pagination args))))

(defn graphql-results [ids db args]
  (let [results (->> (pull-many db full-read
                                ids)
                     (map cleanse))]
    results))


(defn get-graphql-page [args]
  (let [db                                           (dc/db conn)
        {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]

    [(->> (graphql-results ids-to-retrieve db args))
     matching-count]))
