(ns wanijo.infrastructure.neo4j
  (:require [neo4j-clj.core :as db]
            [wanijo.infrastructure.devmode :as devmode]
            [clj-time.format :as time-format]
            [clj-time.local :as time-local]
            [clojure.spec.alpha :as spec]
            [clojure.string :as cljs])
  (:import (java.util UUID)))

(spec/def ::date-str
  #(re-matches #"\d{4}\d{2}\d{2}T\d{2}\d{2}\d{2}\.\d{3}Z" %))

(spec/def ::uuid
  #(re-matches
    #"[0-9a-f]+-[0-9a-f]+-[0-9a-f]+-[0-9a-f]+-[0-9a-f]+" %))

(def conn
  (delay
    (let [env #(or (System/getenv %1) %2)
          port (env "NEO4J_PORT" "7687")
          host (env "NEO4J_HOST" "localhost")
          user (env "NEO4J_USER" "neo4j")
          pass (env "NEO4J_PASS" "b")]
      (db/connect
        (str "bolt://" host ":" port)
        user
        pass))))

(defonce query-strings (atom {}))
(defmacro defquery [name query]
  `(do (def ~name (db/create-query ~query))
       (swap! wanijo.infrastructure.neo4j/query-strings
              assoc (hash ~name) ~query)))

(defn uuid []
  (str (UUID/randomUUID)))

(defn butiful-query [qry]
  (->> (get @query-strings (hash qry) "unknown query")
       str
       cljs/trim-newline
       cljs/split-lines
       (map cljs/trim)
       (filter #(pos? (count %)))
       (cljs/join \newline)))

(defn exec-query! [qry params]
  (with-open [session (db/get-session @conn)]
    (let [query-string (get @query-strings (hash qry) "unknown query")
          query-string (-> query-string
                           (cljs/replace #"\n" "")
                           (cljs/replace #"\s+" " "))
          query-string (str \" query-string \")]
      (println "query" query-string "with params" params)
      (devmode/send-to-bar
        (str (butiful-query qry)
             "<br>---Params---<br>"
             params))
      (qry session params))))

(spec/def ::tuple-query-list
  (spec/coll-of
   (spec/tuple fn? map?)))
(defn exec-queries! [& tuples]
  {:pre [(spec/assert ::tuple-query-list tuples)]}
  (db/with-transaction @conn tx
    (doseq [tuple tuples]
      (let [qry (first tuple)
            params (second tuple)]
        (devmode/send-to-bar
         (str (butiful-query qry)
              "<br>---Params---<br>"
              params))
        (spec/assert map? params)
        (qry tx params)))))

(defn now-str []
  (time-format/unparse
   (time-format/formatters :basic-date-time)
   (time-local/local-now)))
