(ns beherbergung.db.validate
  (:require [clojure.spec.alpha :as s]
            [beherbergung.db.export :refer [all_docs write-edn]]
            [clojure.pprint :refer [pprint]]
            [clojure.tools.logging :refer [error]]))

(defn validate
  "Validate a xtdb-document or a collection of documents.
   When not conforming to the spec, an explaination is associated."
  [doc]
  (if (map? doc)
      (when-not (:xt/fn doc)
                (let [spec (:xt/spec doc)]
                     (when-not spec
                               (error ":xt/spec must not be empty!" doc))
                     (if (s/valid? spec doc)
                         doc
                         (assoc doc :explain (s/explain-data spec doc)))))
      (map validate doc)))

(defn validate-db
  "Validate the database.
   The db_ctx is only returned, when all documents have been confirmed."
  [db_ctx]
  (let [validated-docs (validate (all_docs db_ctx))
        errors (filter :explain validated-docs)
        file "/tmp/validation-errors"]
       (if (not-empty errors)
           (do
             (println "There have been validation errors in" (count errors) "database documents.")
             (println "It seems that the latest update changed this specs:" (into [] (keys (group-by :xt/spec errors))))
             (write-edn file errors)
             (println "Details have been written to:" file))
           db_ctx)))

(defn validate-tx
  "Validate docs before they are written to the database."
  [tx-ops]
  (let [docs (->> tx-ops
                  (filter #(= :xtdb.api/put (first %)))
                  (map second))
        errors (filter :explain (validate docs))]
       (if (not-empty errors)
           (do (println "Transaction canceled due to validiation errors:")
               (pprint errors))
           tx-ops)))
