             [clojure.walk :as walk]
             [spec-tools.data-spec :as ds]
             [expound.ansi :as ansi]
+            [clojure.test.check.random :as random]
+            [clojure.test.check.rose-tree :as rose]
             #?(:clj [orchestra.spec.test :as orch.st]
                :cljs [orchestra-cljs.spec.test :as orch.st])))
 

                   :spec (s/spec ::spec)))

(s/def ::spec-defs (s/coll-of ::spec-def
                              :min-count 1))

(defn exercise-count [spec]
  (case spec

     (doall (s/exercise spec-spec (exercise-count spec-spec)))
     (str "Failed to generate examples for spec " spec-spec))))

(defn sample-seq
  "Return a sequence of realized values from `generator`."
  [generator seed]
  (s/assert some? generator)
  (let [max-size 1
        r (if seed
            (random/make-random seed)
            (random/make-random))
        size-seq (gen/make-size-range-seq max-size)]
    (map #(rose/root (gen/call-gen generator %1 %2))
         (gen/lazy-random-states r)
         size-seq)))

(defn missing-specs [spec-defs]
  (let [defined (set (map second spec-defs))
        used (set
              (filter
               #(and (qualified-keyword? %)
                     (= "expound-generated-spec" (namespace %)))
               (tree-seq coll? seq spec-defs)))]
    (set/difference used defined)))

#?(:clj (deftest eval-gen-test
          (binding [s/*recursion-limit* 2]
            (checking
             "expound returns string"
             5 ;; Hard-code at 5, since generating specs explodes in size quite quickly
             [spec-defs (s/gen ::spec-defs)
              pred-specs (gen/vector (s/gen ::pred-spec) 5)
              seed (s/gen pos-int?)
              ;;:let [spec (second (last spec-defs))]
              ;;valid-form (s/gen spec)
              mutate-path (gen/vector gen/pos-int)
              ;; :let [
              ;;       ]
              other-form gen/any-printable]
             (try
               (doseq [[spec-name spec] (map vector (missing-specs spec-defs) (cycle pred-specs))]
                 (eval `(s/def ~spec-name ~spec)))

               (doseq [spec-def spec-defs]
                 (eval spec-def))

               (let [spec (second (last spec-defs))
                     form (last (last spec-defs))
                     valid-form (if (or
                                     (some
                                      #{;; because of https://dev.clojure.org/jira/browse/CLJ-2152
                                           ;; we can't accurately analyze forms under an '&' spec
                                        "clojure.spec.alpha/&"
                                        "clojure.spec.alpha/fspec"
                                        "clojure.spec.alpha/multi-spec"
                                        "clojure.spec.alpha/with-gen"}
                                      (map str (tree-seq coll? identity form)))

                                     (some
                                      #{;; because of https://dev.clojure.org/jira/browse/CLJ-2152
                                           ;; we can't accurately analyze forms under an '&' spec
                                        "clojure.spec.alpha/&"
                                        "clojure.spec.alpha/fspec"
                                        "clojure.spec.alpha/multi-spec"
                                        "clojure.spec.alpha/with-gen"}
                                      (->> spec
                                           inline-specs
                                           (tree-seq coll? identity)
                                           (map str))))
                                  other-form
                                  (first (sample-seq (s/gen spec) seed)))
                     invalid-form (mutate valid-form mutate-path)]
                 (try

                   (is (string?
                        (expound/expound-str spec invalid-form)))
                   (is (not
                        (clojure.string/includes?
                         (expound/expound-str (second (last spec-defs)) invalid-form)
                         "should contain keys")))
                   (catch Exception e
                     (is (or
                          (string/includes?
                           (:cause (Throwable->map e))
                           "Method code too large!")
                          (string/includes?
                           (:cause (Throwable->map e))
                           "Cannot convert path."))))))
               (finally
                 ;; Get access to private atom in clojure.spec
                 (def spec-reg (deref #'s/registry-ref))
                 (doseq [k (filter
                            (fn [k] (= "expound-generated-spec" (namespace k)))
                            (keys (s/registry)))]
                   (swap! spec-reg dissoc k))))))))

(deftest clean-registry
  (testing "only base spec remains"
