             [clojure.string :as str]
             [cheshire.core :as json]))
 
-(declare lookup-nation lookup-city lookup-year)
+(declare lookup-nation lookup-city)
 
 (def ^:const base-url "https://www.wikidata.org/w/api.php")
 
-(def ^:const properties
-  {:place-of-birth "P19"
-   :date-of-birth "P569"
-   :title "P373" ;; technically "Commons Category". Regardless, seems to be a
+(def ^:const property-keys
+  {:place-of-birth :P19
+   :date-of-birth :P569
+   :title :P373 ;; technically "Commons Category". Regardless, seems to be a
    ;; good way to cut to the chase and get an identifier
    })
+
+(defn property
+  ([key properties]
+     (property key properties nil))
+  ([key properties default]
+     (get properties
+          (or (get property-keys key)
+              (throw (ex-info "Property key doesn't exist"
+                              {:key key
+                               :properties property
+                               :property-keys property-keys})))
+          default)))
 
 (defmacro defasync [name doc args & body]
   `(defn ~name ~doc
      ([~@(drop-last args)]
-        (println "start of single-arity async")
         (let [p# (promise)]
-          (println "init'ed promise for single-arity async")
           (~name ~@(drop-last args) (fn [val#] (p# val#) val#))
-          (println "returning promise for single-arity async")
           p#))
      ([~@args] ~@body)))
 

(defasync base-query
  "GET query to wikidata with the given parameters"
  [params callback]
  (println "aboot to query" params)
  (http/get base-url {:query-params (merge {:language "en"
                                            :format "json"}
                                           params)}
            (comp callback id-print)))

(defn body [http-response]
  (-> http-response (:body) (json/decode true)))

   TODO: figure out why promise is super broken, just use callback for now"
  [id callback]
  (base-query {:action "wbgetclaims" :entity id}
              (comp callback :claims body)))

(defmulti prop-value* (fn [prop _] (:datatype (:mainsnak prop))))

(defn year [string-time]
  (apply str (take 4 (rest string-time))))

(def value (comp :value :datavalue :mainsnak))

(defmethod prop-value* "string"
  [prop callback]
  (callback (value prop)))

(defmethod prop-value* "time"
  [prop callback]
  (-> prop
      (value)
      (:time)
      (year)
      (callback)))

(defmethod prop-value* "wikibase-item"
  [prop callback]
  (let [id (str "Q" (:numeric-id (value prop)))]
    (properties id callback)))

(defasync prop-value
  "Asynchronous, pull proper \"value\" representation out of a given
   item. Extend with prop-value*"
  [p c]
  (prop-value* p c))


#_(defn lookup-year [data]
  (let [who (:name (:composer data))]
    (->> (id-search who)
         (properties)
         (property :date-of-birth)
         (first)
         (prop-value))))

(defn lookup-year [data]
  (let [who (:name (:composer data))
        p (promise)]
    (id-search who
               (fn [id]
                 (properties id
                             (comp #(prop-value % (partial deliver p)) first (partial property :date-of-birth)))))
    p))


;; TODO hey, that shit was fuckin easy, now what to do about place?
;; * from Liszt "Raiding", got the idea that "Commons Category" for
;; place is city, another query 
