(ns claire-agent.agent
  (:require
   [claire-common.utils :as utils]
   [claire-common.dialogflow :as df]
   [claire-agent.intents.purchase-taric :as purchase-taric]
   [claire-agent.intents.purchase-product :as purchase-product]
   [claire-agent.intents.purchase-lookup :as purchase-lookup]
   [claire-agent.intents.purchase-correct :as purchase-correct]
   [claire-agent.intents.purchase-bid :as purchase-bid]
   [clojure.core.async
    :as async
    :refer [>! <! go chan buffer close! put! alts! timeout]])
  (:require-macros
   [claire-common.macros :as macros :refer [<? go-try]]
   [claire-common.flow :as flow]))

(def agent-messages
  "Messages are functions of two arguments: the query and the environment"
  
  {
   "unexpected-error"
   #(str "An unexpected error occurred. Please, try again later." )
   
   :purchase-taric-notaric
   {"Do you know the Taric code?"
    #(str "Sure. Do you know the Taric code of the product?")
    "re-prompt"
    #(str "I did not understand. Do you know the Taric code of the product?")}
   
   :purchase-ask-for-taric-purchase-taric-confirmation
   {"Confirm Taric code"
    #(str "I am now going to lookup Taric code " %1
          ". Is it correct?")
    "re-prompt"
    #(str "Confirm you want to purchase Taric code " %1
          ". Is it correct? Please say yes or no.")}
   
   :purchase-lookup
   {"Start looking for suppliers"
    #(str "Now looking for the best suppliers for your
   product! This may take a while. Shall I continue?")
    "re-prompt"
    #(str "I didn't understand. Shall")}

   :purchase-lookup-purchase-lookup-wait
   {"Still looking for suppliers"
    #(str "I am still looking for the best suppliers. Shall I continue?")}
   
   :purchase-lookup-purchase-lookup-finished
   {"Found suppliers"
    #(str "I have found a bunch of suppliers. "
          "You can now review their offers by saying review bids.")}

   ;; "purchase-done"
   ;; #(str "LOOKUP FINISHED")

   ;; "Fix Taric failed"
   ;; #(str "Now I should have asked you for your taric code, but
    ;; something went wrong.")
   
   :purchase-product
   {"Specify quantity"
    #(let [pars (:state %2)]
       (str "I have got your order for "
            (get pars "productClass")
            ". Specify which quantity, please."))}

   :purchase-product-purchase-confirmation
   {"Confirm your order and lookup"
    #(let [pars (:state %2)]
       (str "I have got your order for " (get pars "productClass") 
            ". Shall I look up the best suppliers "
            "or would you like to correct it?"))
    "re-prompt"
    #(str "I did not understand. You can either correct your order or "
          "search for the best supplier.")}

   ;; "purchase-askfortaric-followup"
   ;; #(str "I am still looking for the best suppliers. "
         ;; "Shall I keep going?")
   
   :purchase-review
   {"Specify value to correct"
    #(let [state (:state %2)]
       (str "Please, specify the new value for your order " %1
            ". Currently, you have: " (get state (get state "parameterName"))
           ", or say: I am done."))}

   ;; "taric-confirmed"
   ;; #(str "Now looking for the best suppliers for your product! "
   ;;       "This may take a while. Shall I continue?")
   
   ;; "lookup-completed"
   ;; #(str "I have found a bunch of potential suppliers!")

   :purchase-review-purchase-correct-parameter
   {"Correct another parameter?"
    #(let [pars (:state %2)
           itemName (get pars "parameterName")
           itemValue %1]
       (str "I have corrected the " itemName
            " of your order to " itemValue
            ". Would you like to correct another item?"))
    "re-prompt"
    #(str "You can either correct another parameter by saying yes, "
          "or move on by saying no.")}

   ;; "not-found-suppliers"
   ;; #(str "Still looking for suppliers! Shall I keep going?")

   :purchase-choose-bidder
   {"The best offer I found is: XYZ. Make a bid?"
    #(str "The best offer I found is $500 from ACME, Inc. "
          "Would you like to make a bid?")
    
    "The best offer I found is: ABC. Make a bid?"
    #(str "The best offer I found is $600 from Food United, Inc. "
          "Would you like to make a bid?")}
   
   :purchase-choose-bidder-purchase-accept-bid
   {"Bid accepted! Will notify when reply comes in"
    #(str "Ok. I sent your bid over. Will notify you when a reply comes in.")}

   :purchase-choose-bidder-purchase-gather-bid
   {"What's your bid XYZ?"
    #(str "What is your bid for ACME, Inc.? They offered $500.")

    "What's your bid ABC?"
    #(str "What is your bid for Food United, Inc.? They offered $600.")}

   :purchase-gather-bid-purchase-bid-confirmation
   {"Confirm bid and send it?"
    #(str "Would you like to confirm your bid to ACME, Inc. for " %1)}

   })

(defn- make-handler [fun]
  (utils/make-handler fun agent-messages))

(defn- welcome [intent query env pars]
  (utils/make-reply
   env
   :say (str "Hello! May I help you? "
             "You can buy or sell meat, beans, or vegetables.")))

(defn- fallback [intent query env pars]
  (let [ctx (utils/find-first #(= 1 (get %1 "lifespan")) (:context env))
        ctx (clojure.string/replace (get ctx "name") #"-followup$" "")
        env (reduce
             #(df/extend-context %1 (get %2 "name"))
             env
             (:context env))
        msgs (get agent-messages (keyword intent))
        msg (or (get msgs "re-prompt")
                (second (first msgs))
                #(str "I didn't understand"))]
    (utils/make-reply env :export true :say (msg query env))))

(def ^:const intents
  (assoc (into {} (for [[k v] (df/intent-map "claire-agent")
                        :when v]
                    [k (make-handler v)]))
    "Default Fallback Intent" (make-handler fallback)
    "Default Welcome Intent" (make-handler welcome)))

(defn make-intents
  ([] (make-intents [purchase-taric/intent
                     purchase-product/intent
                     purchase-correct/intent
                     purchase-bid/intent
                     purchase-lookup/intent]))
  ([intents]
   (go-try
    (let [_ (<? (df/delete-all "Intent"))
          _ (<? (df/delete-all "EntityType"))
          entity-types
          [(df/entity-type "claire_incoterm"
                          [["Ex Works" "EWO"]
                           ["Carriage and Insurance Paid" "CIP"]
                           ["Delivered Duty Paid" "DDP"]
                           ["Delivered at Place" "DAP"]
                           ["Delivered at Terminal" "DAT"]
                           ["Carriage Paid" "CAP"]
                           ["Free Carrier" "FCA"]])
          (df/entity-type "claire_knownUnknown"
                          [["Unknown"
                            "Unknown bidders"
                            "Unknown also"
                            "Unknown bidder also"]
                           ["Known"
                            "Known bidders"
                            "Only known"
                            "Only known bidders"]])
          (df/entity-type "claire_orderParameter"
                          [["quantity" "quantity"]
                           ["destination" "destination" "city"]
                           ["when" "date" "time"]
                            ["productClass" "product"]])]

          _ (<? (df/make-entity-types entity-types))
          intents (<? (df/make-intents intents))
          ]
      intents))))

