(ns debug-sccb-transactions
  (:require [datomic.api :as d]
            [auto-ap.datomic :refer [conn]]
            [auto-ap.utils :refer [dollars=]]
            [auto-ap.time :as atime :refer [iso-date]]
            [clj-time.core :as t]
            [clj-time.coerce :as coerce]
            [clj-time.periodic :as periodic]
            [clojure.data.csv :as csv]

            [auto-ap.yodlee.core :as y]
            [auto-ap.yodlee.core2 :as y2]))

(d/pull (d/db conn) '[*] [:bank-account/code "SCCB-9128CB"])

(def trans (y/get-specific-transactions-with-date 27995674 "2021-01-01" "2021-12-31" (y/get-auth-header)))

(user/init-repl)

[(->> trans
      (sort-by :date)
      (map (fn [t]
             (if (= "DEBIT" (:baseType t))
               (-  (:amount (:amount t)))
               (:amount (:amount t)))))
      (reduce + 0.0))
 (:amount (:runningBalance (first trans)))
 (:amount (:runningBalance (last trans)))
 (- (:amount (:runningBalance (last trans)))
    (:amount (:runningBalance (first trans))))]


(defn y-tx->$ [b]
  (if (= "DEBIT" (:baseType b))
    (-  (:amount (:amount b)))
    (:amount (:amount b))))


(do 
  
  (doseq [bank-account #_ (d/q '[:find [?yai ...]
                                 :in $
                                 :where [_ :bank-account/yodlee-account-id ?yai]]
                               (d/db conn))
          :when        (not= 0 bank-account)]
    (try 
      (println 
        [bank-account  (->> (y/get-specific-transactions-with-date bank-account "2021-01-01" "2021-12-31" (y/get-auth-header))
                            (reverse)
                            (filter :runningBalance)
                            (partition 2 1)
                            (map (fn [[a b]]
                                   (try 
                                     [(:amount (:runningBalance a))
                                      
                                      (- (:amount (:runningBalance b))
                                         (if (= "DEBIT" (:baseType b))
                                           (-  (:amount (:amount b)))
                                           (:amount (:amount b))))
                                      ]
                                     (catch Exception e
                                       (println a b)
                                       (println e)
                                       (throw e)
                                       ))
                                   ))
                            (filter (fn [[a b]]
                                      (not (dollars= a b))))
                            count
                            
                            )])
      (catch Exception e
        (println "please retry" bank-account)))
    )
  (println "done"))



(take 10 trans)

(first trans)
(last trans)


(csv/write-csv *out*
               (->> trans
                    (sort-by :date)
                    (map (fn [z]
                           (update z :date #(auto-ap.time/unparse (auto-ap.time/parse % auto-ap.time/iso-date) auto-ap.time/normal-date))))
                    (map (fn [z]
                           [(:date z) (:original (:description z)) (if (= "DEBIT" (:baseType z))
                                                                                                     (-  (:amount (:amount z)))
                                                                                                     (:amount (:amount z)))]
                           ))
                    
                    )

               :separator \tab
               )

(def deep-dive (y/get-specific-transactions-with-date 27995674 "2021-12-03" "2021-12-03" (y/get-auth-header)))


(csv/write-csv *out*
               (->> (->> (y/get-specific-transactions-with-date 27995674 "2021-01-01" "2021-12-31" (y/get-auth-header))
                         (group-by (fn [d]
                                     ((juxt :description
                                            :amount
                                            :date
                                            :baseType
                                            :type
                                            :isManual
                                            :merchantType
                                            :status
                                            :CONTAINER
                                            :runningBalance 
                                            :subType
                                            :isPhysical
                                            :accountId
                                            :postDate) d)))
                         (map (fn [[k v]]
                                (first (sort-by :id v)))))
                    (sort-by :date)
                    (map (fn [z]
                           (update z :date #(auto-ap.time/unparse (auto-ap.time/parse % auto-ap.time/iso-date) auto-ap.time/normal-date))))
                    (map (fn [z]
                           [(:date z) (:original (:description z)) (if (= "DEBIT" (:baseType z))
                                                                     (-  (:amount (:amount z)))
                                                                     (:amount (:amount z)))]
                           ))
                    
                    )

               :separator \tab
               )

(->> (y/get-specific-transactions-with-date 27995674 "2021-12-30" "2021-12-30" (y/get-auth-header))
                         (group-by (fn [d]
                                     ((juxt :description
                                            :amount
                                            :date
                                            :baseType
                                            :type
                                            :isManual
                                            :merchantType
                                            :status
                                            :CONTAINER
                                            :runningBalance 
                                            :subType
                                            :isPhysical
                                            :accountId
                                            :postDate) d)))
                         (map (fn [[k v]]
                                [k (count v)]
                                  #_(clojure.data/diff a b))))

(y2/get-accounts "DEMO2")


(->> (y2/get-specific-transactions "DEMO2" 15565801)
                         (group-by (fn [d]
                                     ((juxt :description
                                            :amount
                                            :date
                                            :baseType
                                            :type
                                            :isManual
                                            :merchantType
                                            :status
                                            :CONTAINER
                                            :runningBalance 
                                            :subType
                                            :isPhysical
                                            :accountId
                                            :postDate) d)))
                         (map (fn [[k v]]
                                [k (count v)]
                                #_(clojure.data/diff a b)))
                         count)

(csv/write-csv *out*
               (->> (y2/get-specific-transactions "DEMO2" 15565801)
                    (sort-by :date)
                    (map (fn [z]
                           (update z :date #(auto-ap.time/unparse (auto-ap.time/parse % auto-ap.time/iso-date) auto-ap.time/normal-date))))
                    (map (fn [z]
                           [(:date z) (:original (:description z)) (if (= "DEBIT" (:baseType z))
                                                                     (-  (:amount (:amount z)))
                                                                     (:amount (:amount z)))]
                           )))
               :separator \tab)



(def bad-as [24265567
             16422563
             27327396
             16279665
             16279668
             16279669
             16279670
             16279671
             24230113
             16279664
             16421944
             24287315
             27723398
             27723397
             16422358
             18685498
             16422285
             24370905
             18911886
             27995674
             19935858
             16422708])


(def all-as  [
[18409209 0]
[18409210 0]
[27040638 0]
[18409211 0]
[27040639 0]
[27040640 0]
[27398018 0]
[24249175 0]
[16551129 0]
[24781151 0]
[24265567 2433]
[25829735 0]
[25976674 0]
[25829733 0]
[25829734 0]
[26403171 0]
[26403172 0]
[26403169 0]
[26403170 0]
[19679001 0]
[20772618 0]
[26403173 0]
[27509142 0]
[19679000 0]
[18937120 0]
[22730534 0]
[25640840 0]
[16551102 0]
[20186867 0]
[27784044 0]
[26216350 0]
[27784043 0]
[27784042 0]
[21961051 0]
[16422563 48]
[22981959 0]
[16422564 0]
[18356995 0]
[16422562 0]
[27040641 0]
[27040642 0]
[27040643 0]
[26997692 0]
[26997691 0]
[26793947 0]
[27327396 64]
[16279665 83]
[16279666 0]
[26988499 0]
[16279667 0]
[16279668 12]
[16279669 16]
[16279670 55]
[16279671 4]
[16279672 0]
[24230113 80]
[25624533 0]
[25624532 0]
[16279663 0]
[16279664 441]
[26991555 0]
[27798450 0]
[27798451 0]
[26607050 0]
[16422491 0]
[16422492 0]
[16422495 0]
[16422493 0]
[27847951 0]
[16422494 0]
[25646832 0]
[25646831 0]
[16421963 0]
[16422473 0]
[26920161 0]
[16422471 0]
[16422472 0]
[16421945 0]
[16422463 0]
[16422462 0]
[24591293 0]
[16421943 0]
[16421944 8]
[24566177 0]
[24434091 0]
[24434092 0]
[24104846 0]
[24104845 0]
[24104844 0]
[24411026 0]
[24104839 0]
[24104838 0]
[21754236 0]
[27576149 0]
[18099105 0]
[24499586 0]
[18937304 0]
[18937305 0]
[25308479 0]
[25308480 0]
[24104849 0]
[23662442 0]
[25308484 0]
[25308481 0]
[16876498 0]
[24265569 0]
[16876499 0]
[24265570 0]
[24287315 317]
[26983548 0]
[24287316 0]
[27723399 0]
[27723398 21]
[27723397 110]
[24287319 0]
[27576450 0]
[24287317 0]
[27576451 0]
[24287318 0]
[26983549 0]
[27576452 0]
[26794100 0]
[24287326 0]
[16422358 13]
[17303033 0]
[26942060 0]
[24776257 0]
[16876551 0]
[22883931 0]
[17970207 0]
[27361904 0]
[27361903 0]
[19941347 0]
[27345526 0]
[16417200 0]
[16428453 0]
[19756531 0]
[16428443 0]
[18685498 18]
[27465416 0]
[27465415 0]
[27465417 0]
[16422296 0]
[27847900 0]
[27847902 0]
[16422285 11]
[25350341 0]
[16428403 0]
[16551286 0]
[27282619 0]
[27282620 0]
[19942033 0]
[19942034 0]
[26220018 0]
[27780614 0]
[27793421 0]
[17303150 0]
[27793422 0]
[16428372 0]
[24370905 450]
[16428375 0]
[18911886 44]
[16428373 0]
[16428374 0]
[18911888 0]
[27995676 0]
[27995675 0]
[27995674 861]
[19942029 0]
[17408629 0]
[19942030 0]
[22219439 0]
[19942031 0]
[19942032 0]
[25543675 0]
[19935858 606]
[16422707 0]
[16422708 320]
[27116551 0]
[16428317 0]
[16428318 0]
[24510110 0]
[27847749 0]
[654654654 0]
[21489791 0]
[21489790 0]
[23139427 0]
[16422643 0]
[16422644 0]
[16422642 0]
[16428279 0]
[25247822 0]
[18409174 0]
[16422645 0]
[18948276 0]
[18948275 0]
])
(filter (fn [[_ x]] (not= 0 x)) all-as)

(->> (d/q '[:find [(pull ?ba [:bank-account/name :bank-account/code]) ...]
            :in $ [?yai ...]
            :where [?ba :bank-account/yodlee-account-id ?yai]]
          (d/db conn)
          
          )
     (map (juxt :bank-account/name :bank-account/code)))


(def trans (y/get-specific-transactions-with-date 27995674 "2021-01-01" "2021-12-31" (y/get-auth-header)))

(def starting (:amount (:runningBalance (last trans))))

(def dates (sort (set (map :date trans))))


(defn find-mistakes [trans-set]
  (let [sequences (->> trans-set
                       (filter :runningBalance)
                       (map (fn [index t]
                              {:index index
                               :id (:id t)
                               :starting (- (:amount (:runningBalance t))
                                            (if (= "DEBIT" (:baseType t))
                                              (-  (:amount (:amount t)))
                                              (:amount (:amount t))))
                               :amount   (if (= "DEBIT" (:baseType t))
                                         (-  (:amount (:amount t)))
                                         (:amount (:amount t)))
                               :after    (:amount (:runningBalance t))
                               :original t}
                              ) (range)))
        graph (reduce 
                (fn [graph s]
                  (assoc graph s
                         (filter (fn [s2]
                                   (dollars= (:starting s2)
                                             (:after s))
                                   ) sequences)
                         )
                  )
                {}
                sequences)
        mistakes (filter
                   (fn [[k v]]
                     (> (count v) 1)
                     )
                   graph)]
    
    (->> mistakes
         (map second)
         (map (fn [duplicates]
                   (map :original duplicates)))
         )))

(csv/write-csv *out* 
               (for [a    bad-as
                     :let [{:bank-account/keys [name code]} (first (d/q '[:find [(pull ?ba [:bank-account/name :bank-account/code]) ...]
                                                                          :in $ ?yai
                                                                          :where [?ba :bank-account/yodlee-account-id ?yai]]
                                                                        (d/db conn)
                                                                        a))
                           transactions  (y/get-specific-transactions-with-date a "2021-01-01" "2021-12-31" (y/get-auth-header))]

                     
                     mistakes (->> transactions
                                   (group-by :date)
                                   vals
                                   (mapcat find-mistakes))
                     ]
                 
                 [code
                  name
                  (:date mistakes)
                  (:amount (:amount mistakes))])
               :separator \tab)

(def mistake-set 
  (doall 
    (for [a    bad-as
          :let [{:bank-account/keys [name code]} (first (d/q '[:find [(pull ?ba [:bank-account/name :bank-account/code]) ...]
                                                               :in $ ?yai
                                                               :where [?ba :bank-account/yodlee-account-id ?yai]]
                                                             (d/db conn)
                                                             a))
                transactions  (y/get-specific-transactions-with-date a "2021-01-01" "2021-12-31" (y/get-auth-header))]

          
          ]
      
      [code 
       (->> transactions
            (group-by :date)
            vals
            (map find-mistakes)
            (filter seq))])))

(user/init-repl)

mistake-set


(csv/write-csv *out* 
               (mapcat identity 
                       (for [[[client mistake-set] i] (map vector mistake-set (range))
                             [mistake-set i2]          (map vector mistake-set (range))
                             [mistake-set i3]          (map vector mistake-set (range))
                             :let                 [already-fixed? (= (->> mistake-set
                                                                          (map #(d/pull (d/db conn)
                                                                                        [:db/id {:transaction/approval-status [:db/ident]}]
                                                                                        [:transaction/id
                                                                                         (digest/sha-256 (str(:id %)))])
                                                                               )
                                                                          (filter :db/id)
                                                                          (filter #(and (not= :transaction-approval-status/suppressed (:db/ident  (:transaction/approval-status %)) )
                                                                                        (not= :transaction-approval-status/exclude-from-ledger (:db/ident  (:transaction/approval-status %)))))
                                                                          count)
                                                                     1)]]

                         (mapv
                           (fn [m]
                             (let [iol-transaction (d/pull (d/db conn)
                                                           [:db/id {:transaction/approval-status [:db/ident]
                                                                    :transaction/bank-account    [:bank-account/code]}]
                                                           [:transaction/id
                                                            (digest/sha-256 (str(:id m)))])]
                               [(str i "-" i2 "-" i3)
                                (or (:bank-account/code (:transaction/bank-account iol-transaction))
                                    "not found")
                                (:date m)
                                (:amount (:amount m))
                                (:id m)
                                (or (:db/id iol-transaction) "not found")
                                (or (some-> (:db/ident (:transaction/approval-status iol-transaction))
                                            name)
                                    "not found")
                                already-fixed?
                                ]
                               ))
                           mistake-set)))

               :separator \tab)

(->> trans 
     (reverse)
     (filter :runningBalance)
     (partition 2 1)
     (map (fn [[a b]]
            (try 
              [
               (- starting
                  (:amount (:runningBalance a)))
               
               
               (- starting
                 (:amount (:runningBalance b))
                  (if (= "DEBIT" (:baseType b))
                    (-  (:amount (:amount b)))
                    (:amount (:amount b))))
               ]
              (catch Exception e
                (println a b)
                (println e)
                (throw e)
                ))
            ))
     (filter (fn [[a b]]
               (not (dollars= a b))))
     )

(csv/write-csv *out* 
               (->> trans
                    (reverse)
                    (map (fn [t]
                           [(:date t)
                            (:amount (:runningBalance t))
                            (if (= "DEBIT" (:baseType t))
                              (-  (:amount (:amount t)))
                              (:amount (:amount t)))]
                           )))
               :separator \tab)



(def ts (auto-ap.intuit.core/get-transactions "2022-02-01" "2022-02-05" "BCFM - Heritage Main 7362 ---BCFM-H7362"))

(count ts)



(defn check-transactions [db cc bac ba yba]
    (let [all-transactions (d/q '[:find ?d ?a ?tx
                                  :in $ ?ba
                                  :where [?tx :transaction/bank-account ?ba]
                                  [?tx :transaction/date ?d]
                                  [(>= ?d #inst "2021-01-01T00:00:00-08:00")]
                                  [(<= ?d #inst "2021-12-31T00:00:00-08:00")]
                                  (not [?tx :transaction/approval-status :transaction-approval-status/suppressed])
                                  (not [?tx :transaction/approval-status :transaction-approval-status/excluded])
                                  [?tx :transaction/amount ?a]]
                                db ba)
          not-excluded (fn [t]
                         (not (y/known-bad-yodlee-ids (:id t))))
          all-yodlee-transactions (->> (y/get-specific-transactions-with-date yba "2021-01-01" "2021-12-31" (y/get-auth-header))
                                       (filter not-excluded))
          transactions-by-date (->> all-transactions
                                    (group-by first)
                                    (map (fn [[k v]]
                                           [(atime/unparse-local (coerce/to-date-time k) iso-date) v]))
                                    (into {}))
          yodlee-transactions-by-date (->> all-yodlee-transactions
                                           (group-by :date)
                                           (into {}))
          yodlee-last-updated (-> (y/get-account yba (y/get-auth-header)) first :dataset first :lastUpdated)

          ]
      (for [d (->> 
                (periodic/periodic-seq (coerce/to-date-time #inst "2021-01-01T00:00:00-08:00") (t/days 1))
                (map (fn [d]
                       (atime/unparse d iso-date)))
                (take 365))
            
            :let    [txs (transactions-by-date d)
                     y-txes (yodlee-transactions-by-date d)
                     y-total (reduce + 0.0 (map y-tx->$ y-txes))
                     t-total (reduce + 0.0 (map (fn [[_ a]
                                                     ]
                                                  a)
                                                txs))]]
        
        (into 
          [cc bac
           yba d
           (count txs ) (count y-txes)
           t-total y-total]

          (cond
            (and 
              (= (count txs ) (count y-txes) 0)
              (>= (.compareTo d yodlee-last-updated) 0))
            ["Match but yodlee feed stopped"
             (format "Date %s is after yodlee feed ended (%s)", d yodlee-last-updated)]

            (and 
              (= (count txs ) (count y-txes))
              (dollars= t-total y-total))
            ["Perfect Match"
             ""]
            

            (>= (.compareTo d yodlee-last-updated) 0)
            ["Mismatched because yodlee feed stopped"
             (format "Date %s is after yodlee feed ended (%s)", d yodlee-last-updated)]

            (not= (count txs ) (count y-txes))
            ["Mismatched number of transactions"
             ""]

            (not (dollars= t-total y-total))
            ["Transactions don't add up"
             ""]

            :else
            ["Mismatch"
             "Mismatch, unknown cause"]
            ))
        )))


(do ;;find iol duplicates
  
  #_(check-transactions (d/db auto-ap.datomic/conn) 17592188489475 18911886 )

  (csv/write-csv *out* 
                 (let [db (d/db auto-ap.datomic/conn)]
                   (for [[ba yba bac cc] (d/q '[:find ?ba ?yba ?bac ?cc
                                                :in $
                                                :where [?ba :bank-account/yodlee-account-id ?yba]
                                                [(not= ?yba 0)]
                                                [?ba :bank-account/code ?bac]
                                                [?c :client/bank-accounts ?ba]
                                                [?c :client/code ?cc]]
                                              db
                                              )
                         row             (try (check-transactions db cc bac ba yba)
                                              (catch Exception _
                                                []))]
                     row))
                 :separator \tab)

  )
