             [cmr.message-queue.services.queue :as queue]
             [cmr.message-queue.config :as iconfig]
             [cmr.common.log :as log :refer (debug info warn error)]
-            [clojure.set :as set]))
+            [clojure.set :as set]
+            [cmr.common.util :as util]))
+
+(def message-queue-history
+  "A vector of message queue maps. A new message queue map is added every time an action takes
+  place on the message queue. A message queue map contains an action and a list of
+  messages.  See message-queue-history-example for an example."
+  (atom []))
+
+(def valid-action-types
+  "A set of the valid action types for a message queue"
+  #{:reset
+    :enqueue
+    :process})
+
+(defn create-message-queue-history-entry
+  "Create a message queue history map. Takes an action, the data associated with that action
+  and the result of that action."
+  [action-type data resulting-state]
+  (when-not (valid-action-types action-type)
+    (throw (Exception. (str "Unknown action-type: " action-type))))
+  (let [data-with-state (when (seq data) (assoc data :state resulting-state))
+        messages (case action-type
+                   :reset []
+                   (:enqueue :process)
+                   (when (seq data)
+                     (-> (peek @message-queue-history)
+                         :messages
+                         ;; Replace the given concept ID and revision ID with the updated message
+                         (->> (remove #(and (= (:concept-id data) (:concept-id %))
+                                            (= (:revision-id data (:revision-id %))))))
+                         (conj data-with-state))))
+        new-action (util/remove-nil-keys {:action-type action-type
+                                          :data data-with-state})]
+    {:action new-action
+     :messages messages}))
+
+
+(defn update-message-queue-history
+  "TODO"
+  [action-type data resulting-state]
+  (swap! message-queue-history
+         conj
+         (create-message-queue-history-entry action-type data resulting-state)))
+
+(comment
+  (update-message-queue-history :enqueue
+                                {:concept-id "C3-PROV1"
+                                 :revision-id 1
+                                 :id 1}
+                                :initial)
+
+  (update-message-queue-history :reset
+                                {}
+                                nil)
+
+  (:messages (peek @message-queue-history))
+  )
+
+(def message-queue-history-example
+  [
+   ;; initial message queue state
+   {:action {:action-type :reset}
+    :messages []}
+
+   ;; message put on the queue
+   {:action {:action-type :enqueue
+             :data {:concept-id "C1-PROV1"
+                    :revision-id 1
+                    :id 1
+                    :state :initial}}
+    :messages [{:concept-id "C1-PROV1"
+                :revision-id 1
+                :id 1
+                :state :initial}]}
+
+   ;; message put on the queue
+   {:action {:action-type :enqueue
+             :data {:concept-id "C2-PROV1"
+                    :revision-id 1
+                    :id 2
+                    :state :initial}}
+    :messages [{:concept-id "C1-PROV1"
+                :revision-id 1
+                :id 1
+                :state :initial}
+               {:concept-id "C2-PROV1"
+                :revision-id 1
+                :id 2
+                :state :initial}]}
+
+   ;; Successfully process one
+   {:action {:action-type :process
+             :data {:concept-id "C1-PROV1"
+                    :revision-id 1
+                    :id 1
+                    :state :initial}}
+    :response {:status :ok}
+    :messages [{:concept-id "C1-PROV1"
+                :revision-id 1
+                :id 1
+                :state :processed}
+               {:concept-id "C2-PROV1"
+                :revision-id 1
+                :id 2
+                :state :initial}]}
+   ])
+
+(comment
+
+
+  (defn current-states
+    []
+    (map :state (:messages (peek message-queue-history-example))))
+
+  (current-states)
+
+  (defn messages+id->message
+    [messages id]
+    (first (filter #(= id (:id %)) messages)))
+
+  (defn concept-history
+    "Returns a map of concept id revision id tuples to the sequence of states for each one"
+    []
+    (let [int-states (for [mq message-queue-history-example
+                           :when (not= (get-in mq [:action :action-type]) :reset)
+                           :let [{{:keys [action-type]
+                                   {:keys [concept-id revision-id id]} :data} :action} mq
+                                 result-state (:state (messages+id->message (:messages mq) id))]]
+                       {[concept-id revision-id] [{:action action-type :result result-state}]})]
+      (apply merge-with concat int-states)))
+
+  (concept-history)
+
+  {["C2-PROV1" 1] [{:action :enqueue, :result :initial}],
+   ["C1-PROV1" 1] ({:action :enqueue, :result :initial}
+                   {:action :process, :result :processed})}
+  )
+
+
 
 (def message-id-key
   "Key used to track messages within a message map. Each message will have a unique id."


      ;; Set the initial state of the message to :initial
      (set-message-state this tagged-msg :initial)

      ;; delegate the request to the wrapped broker
      (queue/publish queue-broker queue-name tagged-msg)))

      (queue/reset queue-broker)
      (reset! message-state-atom {})
      (reset! id-sequence-atom 0)
      (reset! message-queue-history [])
      (finally
        (reset! resetting?-atom false)))))

(defn wait-for-indexing
  "Wait for all messages to be marked as processed"
  [broker-wrapper]
  ; (Thread/sleep 10)
  ; nil)
  (wait-for-states broker-wrapper [:processed]))

;; TODO
#_(def valid-message-modes
    "A list of the modes in which the message queue broker can operate.
    :normal - message functions are called and processed as they normally would.
    :retry - all messages return a retry when they are processed."
    #{:normal
      :retry})

;; TODO
#_(defn set-message-mode
    "Used to toggle message queue between normal mode and failure mode. In normal mode messages are
    processed normally. In failure mode all messages return failures. The failure mode is useful for
    automated tests verifying specific behaviors on failure."
    [mode]
    (if (valid-message-modes mode)
      ;; Use an atom to set state?
      "TODO"
      (throw (Exception. (str "Invalid message queue mode: " mode)))))


(defn create-queue-broker-wrapper
