(ns iol-ion.tx.upsert-ledger
  (:import [java.util UUID Date])
  (:require [datomic.api :as dc]))

(defn -random-tempid []
  (dc/tempid :db.part/user))

(defn get-line-items-after [db journal-entry]
  (for [jel      (:journal-entry/line-items journal-entry)
        next-jel (->> (dc/index-pull db {:index    :avet
                                         :selector [:db/id :journal-entry-line/client+account+location+date]
                                         :start    [:journal-entry-line/client+account+location+date
                     (:journal-entry-line/client+account+location+date jel)
                                                    (:db/id jel)]})
                      (take-while (fn line-must-match-client-account-location [result]
                                    (and
                                     (= (take 3 (:journal-entry-line/client+account+location+date result))
                                        (take 3 (:journal-entry-line/client+account+location+date jel)))
                                     (not= (:db/id jel)
                                           (:db/id result)))))
                      (take 2))
        :when    next-jel]
    (:db/id next-jel)))

(def extant-read '[:db/id :journal-entry/date :journal-entry/client {:journal-entry/line-items [:journal-entry-line/account :journal-entry-line/location :db/id :journal-entry-line/client+account+location+date]}])


(defn current-date [db]
  (let [ last-tx (dc/t->tx (dc/basis-t db))
        [[date]] (seq (dc/q '[:find ?ti :in $ ?tx
                              :where [?tx :db/txInstant ?ti]]
                            db
                            last-tx))]
    date))

(defn calc-client+account+location+date [je jel]
  [(or
    (:db/id (:journal-entry/client je))
    (:journal-entry/client je))
   (or (:db/id (:journal-entry-line/account jel))
       (:journal-entry-line/account jel))
   (-> jel :journal-entry-line/location)
   (-> je :journal-entry/date)])

(defn upsert-ledger [db ledger-entry]
  (assert (:journal-entry/date ledger-entry) (format "Must at least provide date when updating ledger: %s" (pr-str ledger-entry)))
  (assert (:journal-entry/client ledger-entry) "Must at least provide client when updating ledger")
  ;; TODO these are not always true
  ;; (assert (every? :journal-entry-line/account (:journal-entry/line-items ledger-entry)) "must at least provide account when updating ledger")
  ;; (assert (every? :journal-entry-line/location (:journal-entry/line-items ledger-entry)) "Must at least provide location when updating ledger")
  (let [extant-entry         (or (when-let [original-entity (:journal-entry/original-entity ledger-entry)]
                                   (dc/pull db extant-read [:journal-entry/original-entity original-entity]))
                                 (when-let [external-id (:journal-entry/external-id ledger-entry)]
                                   (dc/pull db extant-read [:journal-entry/external-id external-id]))) ]

    (cond->
     [[:upsert-entity (into (-> ledger-entry
                                (assoc :db/id (or
                                               (:db/id ledger-entry)
                                               (:db/id extant-entry)
                                               (-random-tempid)))
                       (update :journal-entry/line-items
                                        (fn [lis]
                                          (mapv #(-> %
                                                     (assoc :journal-entry-line/date (:journal-entry/date ledger-entry))
                                                     (assoc :journal-entry-line/client (:journal-entry/client ledger-entry))) 
                                                lis)))))]
      {:db/id (:journal-entry/client ledger-entry)
       :client/ledger-last-change (current-date db)}])))

