(ns claire-agent.api
  (:require 
   [claire-common.utils :as utils]
   [clojure.core.async
    :as async
    :refer [>! <! go chan buffer close! put! alts! timeout]])
  (:require-macros
   [claire-common.macros :as macros :refer [<? go-try]]))

(def ^:private ^:dynamic *db*
  (atom 
   {:products
    [{:product-id "1"
      :name "chicken wings"
      :seller "Acme, Inc."
      :category "meat"
      :price 100
      :description ""
      :taric-code 12345
      :min-kg 100
      :max-kg 4000
      :min-batch 1
      :kg-batch 100
      :on-demand true
      :incoterm :ex-works
      :is-frozen false
      :origin-country "Spain"}
     {:product-id "2"
      :name "chicken wings"
      :seller "Food Global, Inc."
      :category "meat"
      :price 101
      :description ""
      :taric-code 12345
      :min-kg 100
      :max-kg 5000
      :min-batch 1
      :kg-batch 100
      :on-demand true
      :incoterm :ex-works
      :is-frozen false
      :origin-country "Spain"}
     {:product-id "3"
      :name "pork cheeks"
      :seller "Food Global, Inc."
      :category "meat"
      :price 101
      :description ""
      :taric-code 23456
      :min-kg 100
      :max-kg 5000
      :min-batch 1
      :kg-batch 100
      :on-demand true
      :incoterm :ex-works
      :is-frozen false
      :origin-country "Spain"}]
    :orders
    [{:order-id 1
      :seller "Acme, Inc."
      :buyer "buyer-1"
      :product "chicken wings"
      :status "received"
      :fee-rate 10
      :quantity 234
      :delivery-date "2019-11-12"
      :load-date "2019-11-11"
      :country "China"
      :destination "Madrid"
      :incoterm :ex-works
      :price 222
      :taric-code 12345}
     {:order-id 1
      :seller "Acme, Inc."
      :buyer "buyer-1"
      :product "pork cheeks"
      :status "received"
      :fee-rate 10
      :quantity 234
      :delivery-date "2019-11-12"
      :load-date "2019-11-11"
      :country "China"
      :destination "Madrid"
      :incoterm :ex-works
      :price 222
      :taric-code 23456}]
    :queries
    {"00000" {:query-id "00000"
              :taric-code ""
              :product ""
              :quantity ""
              :destination ""
              :delivery-date ""
              :incoterm :ex-works
              :status "polling"}
     "00001" {:query-id "00001"
              :taric-code ""
              :product ""
              :quantity ""
              :destination ""
              :delivery-date ""
              :incoterm :ex-works
              :status "polling"}}}))

;; Long query simulation

(defn- record-matcher [record]
  (fn [with]
    (loop [r record]
      (let [f (first r)]
        (if (nil? f)
         true
         (if (not= (get with (keyword (first f))) (second f))
           false
           (recur (rest r))))))))

(defn- rfind [key what]
  (let [l (if (sequential? (get @*db* key))
            (get @*db* key)
            (vals (get @*db* key)))]
   (utils/find-first (record-matcher what) l)))

(defn- query-status [qid]
  (:status (rfind :queries {:query-id qid})))

(defn- query-started? [qid]
  (= "polling "(query-status qid)))

(defn- query-completed? [qid]
  (= "finished" (query-status qid)))

(defn- update-query [qid query]
  (swap! *db* assoc :queries
         (assoc (:queries @*db*) qid query)))

(defn- long-call [& [delay query]]
  (let [_ (print "DB: " (:queries @*db*))
        qid (or (:query-id query) (str (random-uuid)))]
    (println "LONG-CALL: started 0 " qid " -- found: " (get (:queries @*db*) qid))
    (when (and (false? (query-started? qid)) (some? delay))
      (update-query qid {:query-id qid :status "polling"})
      (println "EXTERNAL QUERY STARTED -- status: " (get (:queries @*db*) qid))
      (js/setTimeout #(do (println "EXTERNAL QUERY END at "
                                   (.toISOString (js/Date.)))
                          (update-query qid {:query-id qid :status "finished"})
                          delay)))
    (get (:queries @*db*) qid)))

(defn- request [session-id query record]
  (go-try {:session session-id,
           :endpoint query
           :res (rfind query record)}))

(defn look-for-product [session-id product]
  (go-try
   (<? (request session-id :products product))))

(defn look-for-order [session-id order]
  (go-try
   (<? (request session-id :orders order))))

(defn start-query [session-id query]
  (long-call 3500 query))

(defn query [session-id query]
  (long-call nil query))

(def ^:const exports
  {:lookForProduct (utils/make-promise look-for-product)
   :lookForOrder (utils/make-promise look-for-order)
   :startQuery (utils/js-wrapper start-query)
   :query (utils/js-wrapper query)})

