(ns jepsen.antietcd.register
  (:require
    [jepsen
      [checker :as checker]
      [client :as client]
      [generator :as gen]
      [independent :as independent]
    ]
    [jepsen.antietcd.client :as anticli]
    [jepsen.checker.timeline :as timeline]
    [knossos.model :as model]
  )
)

(defn r   [_ _] {:type :invoke, :f :read, :value nil})
(defn w   [_ _] {:type :invoke, :f :write, :value (str (rand-int 5))})
(defn cas [_ _] {:type :invoke, :f :cas, :value [(str (rand-int 5)) (str (rand-int 5))]})

(defrecord Client [url]
  client/Client
  (open! [this test node] (assoc this :url (anticli/node-url node)))

  (setup! [this test])

  (invoke! [this test op]
    (let [[k v] (:value op)]
      (case (:f op)
        :read {:type :ok, :value (independent/tuple key
          (:value (first (:kvs (:body
            (anticli/retry_all #(anticli/antietcd_request (str url "/v3/kv/range") {:key key}))
          ))))
        )}
        :write (merge op (anticli/throw #(anticli/antietcd_put url k v)))
        :cas (let [[old new] v]
          (merge op (anticli/throw #(anticli/antietcd_cas url k old new)))
        )
      )
    )
  )

  (teardown! [this test])

  (close! [_ test])
)

(defn workload
  "A generator, client, and checker for a register test."
  [opts]
  {
    :client (Client. nil)
    :checker (checker/compose
      {
        :perf  (checker/perf)
        :indep (independent/checker
          (checker/compose {
            :linear (checker/linearizable {
              :model     (model/cas-register)
              :algorithm :linear
            })
            :timeline (timeline/html)
          })
        )
      }
    )
    :generator (independent/concurrent-generator
      10
      (map #(str "key" %) (range))
      (fn [k]
        (->>
          (gen/mix [r w cas])
          (gen/stagger (/ (:rate opts)))
          (gen/limit (:ops-per-key opts))
        )
      )
    )
  }
)
