        (map add-required-keys)
        (map add-error-type)))
 
+
+;; *** combine errors for single location
 ;; Sometimes there are multiple errors on the same path location
 
 (defn combined-or-pred [errors]

    `(strictly-specking.strict-keys/strict-mapkeys-impl
      (strictly-specking.parse-spec/parse-keys-args ~@args) ~form)))










;; * moving from errors to messages

;; ** filtering errors

               *print-level* 2]
       (pp/pprint v)))))

;; *** TODO figure out how to identify where to point to

;; might not need this yet

(defn parent [e]
  (get-in (::root-data e) (butlast (:in e))))

(defn current [e]
  (get-in (::root-data e) (:in e)))

(defn parent-pred [pred]
  #(pred (parent %)))

(def parent-sequence? (parent-pred sequential?))
(def parent-vector? (parent-pred vector?))
(def parent-map? (parent-pred map?))
(def parent-set? (parent-pred set?))
(def parent-coll? (parent-pred coll?))

(defmulti error-message ::error-type)

(defmethod error-message :default [e]
  (message-default-str e #_(select-keys e [:pred :path :val :reason :via :in])))

(defn parent-type-str [e]
  (cond
    (parent-map? e)      "Map"
    (parent-set? e)      "Set"
    (parent-vector? e)   "Vector"
    (parent-sequence? e) "Sequence"))

(defn bad-value-message [{:keys [path pred val reason via in] :as e}]
  (let [k (last in)]
    (cond
      (parent-map? e)
      (str "The key " (color (pr-str k) :focus-key)
           " has the wrong value "
           (color (format-summarized-value val) :bad-value)
           ".\n  " (format-predicate-str e))
      (parent-coll? e)
      (str "The " (parent-type-str e)
           " at " (color (pr-str (vec (butlast in))) :focus-path)
           " contains bad value "
           (color (format-summarized-value val) :bad-value)
           ".\n  " (format-predicate-str e))
      :else
      (str "The key " (color (pr-str k) :focus-key)
           " has the wrong value "
           (color (format-summarized-value val) :bad-value)
           ".\n  " (format-predicate-str e)))))

(defmethod error-message ::bad-value [e] (bad-value-message e))
(defmethod error-message ::bad-value-comb-pred [e] (bad-value-message e))

(defmethod error-message ::should-not-be-empty [{:keys [path pred val reason via in] :as e}]
  (str "The value " (color (pr-str val) :bad-value)
       " at key " (color (last in) :focus-key)
       " should not be empty.\n  "
       (format-predicate-str (update-in e [:pred] (fn [x] (list '+ x))))))

(defmethod error-message ::unknown-key [{:keys [path pred val reason via in] :as e}]
  (str "Found unrecognized key " (color (::unknown-key e) :error-key)
       " at path " (color (pr-str in) :focus-path) "\n"
       "  Must be one of: " (format-seq-with-or pred)))

(defmethod error-message ::missing-required-keys [{:keys [path pred val reason via in] :as e}]
  (when-let [kys (not-empty (::missing-keys e))]
    (if (< 1 (count kys))
      (str "Missing required keys " (format-seq-with-and kys) " at path " (color (pr-str in) :focus-path))
      (str "Missing required key "  (format-seq-with-and kys) " at path " (color (pr-str in) :focus-path)))))

;; *** TODO ::wrong-key
;; upon reflection misspelling and wrong keys should have multiple options for correction
;; we probably should move ::correct-key to ::correct-keys
;; but the scores of the top choices should be close and in order 

(defmethod error-message ::wrong-key [{:keys [path pred val reason via in] :as e}]
  (str "The key " (color (::wrong-key e) :error-key)
       " is unrecognized. Perhaps you meant "
       (color (::correct-key e) :correct-key)
       "?"))

;; *** TODO ::misspelled-key
;; upon reflection misspelling and wrong keys should have multiple options for correction
;; we probably should move ::correct-key to ::correct-keys

(defmethod error-message ::misspelled-key [{:keys [path pred val reason via in] :as e}]
  (str "The key " (color (::misspelled-key e) :error-key)
       " is misspelled. It should probably be "
       (color (::correct-key e) :correct-key)))

;; *** TODO: fill in the rest of the error types

(defmulti inline-message ::error-type)

(defmethod inline-message :default [e] (str "The key " (-> e :in last) " has a problem"))
(defmethod inline-message ::bad-value [e]
  (str "The key " (-> e :in last) " has a bad value"))

(defmethod inline-message ::bad-value-comb-pred [e]
  (str "The key " (-> e :in last) " has a bad value"))

(defmethod inline-message ::should-not-be-empty [e]
  (str "The key " (-> e :in last) " should not be empty"))

(defmethod inline-message ::unknown-key [e]
  (str "The key " (::unknown-key e) " is unrecognized"))

;; this is interestion I need to add a key
#_(defmethod inline-message ::missing-required-keys [e]
  (str "XXXXX "
       (-> e :in last)
       " is missing"))

(defmethod inline-message ::wrong-key [e]
  (str "The key " (-> e ::wrong-key) " should probably be " (::correct-key e)))

(defmethod inline-message ::misspelled-key [e]
  (str "The key " (-> e ::misspelled-key) " should probably be " (::correct-key e)))


;; *** use pprint to print contextual errors

(defn pprint-sparse-path
  ([data path key-message-map colors]
   (pp/with-pprint-dispatch annot/error-path-dispatch
     (pp/pprint
      (annot/annotate-path-only
       data
       path
       {:abbrev true
        :comments (into {}
                        (map (fn [[k message]]
                               [k
                                (merge {:key-colors     [:error-key]
                                        :value-colors   [:reset]
                                        :comment-colors [:pointer]
                                        :comment message}
                                       colors)])
                             key-message-map))}))))
  ([data path key-message-map]
   (pprint-sparse-path data path key-message-map {})))

;; *** pprint inline message

(defmulti pprint-inline-message ::error-type)

(defmethod pprint-inline-message :default [e]
  (pprint-sparse-path (::root-data e) (butlast (:in e))
                      {(last (:in e)) (inline-message e)}
                      {:key-colors [:highlight]
                       :value-colors [:bad-value]}))

(defmethod pprint-inline-message ::unknown-key [e]
  (pprint-sparse-path (::root-data e) (:in e)
                      {(::unknown-key e) (inline-message e)}))

(defmethod pprint-inline-message ::misspelled-key [e]
  (pprint-sparse-path (::root-data e) (:in e)
                      {(::misspelled-key e) (inline-message e)}))

(defmethod pprint-inline-message ::wrong-key [e]
  (pprint-sparse-path (::root-data e) (:in e)
                      {(::wrong-key e) (inline-message e)}))

(defmethod pprint-inline-message ::missing-required-keys [e]
  (pprint-sparse-path (reduce #(assoc-in %1 (conj (vec (:in e)) %2)
                                         '...)
                              (::root-data e)
                              (::missing-keys e))
                      (:in e)
                      (into {}
                            (map (fn [k]
                                   [k (str "The required key " (pr-str k) " is missing")]))
                            (::missing-keys e))))

(defn test-print [error]
  (println (color "---------------------------------\n" :header))
  (println (error-message error))
  (println "\n")
  ;; could should indent this
  (pprint-inline-message error)
  (println "\n")
  (println (color "---------------------------------\n" :footer))  
  )

(do
  (def structer [{:compiler 1
                :id 1
                :asdfasdf [1 2]
                ;:source-paths [""]
                }])

  (def terr (s/explain-data :fig-opt/builds structer))

  (with-ansi
    (->> 
     (prepare-errors terr structer )
     #_(map pprint-inline-message)
     (mapv test-print)
     #_ (mapv error-message)
     #_ (map println)))
  )

;; pprint is in annotated-pprint.clj

