(ns auto-ap.views.components.typeahead
  (:require [reagent.core :as r]
            [reagent.ratom :as ra]
            [clojure.string :as str]
            [clojure.set :as set]
            [downshift :as ds :refer [useCombobox]]
            [react]))

(set! *warn-on-infer* true)

;; TODO: This avoids the use of inferred externs by using aget. You could just use the ^js tag though
(defn state-reducer [state actions-and-changes]
  (cond
    (= (aget actions-and-changes "type") (aget (aget useCombobox "stateChangeTypes" ) "InputChange"))
    (doto (aget actions-and-changes "changes") (aset "selectedItem" nil))

    (and (= (aget actions-and-changes "type") (aget (aget useCombobox "stateChangeTypes") "InputBlur"))
         (not (aget state "selectedItem")))
    (doto (aget actions-and-changes "changes" ) (aset "inputValue" nil))

    :else
    (aget actions-and-changes "changes")))

(defn typeahead-v3-internal [{:keys [class style disabled entities ^js entity->text entities-by-id entity-index on-change disabled value name auto-focus] :or {disabled false} :as i}]
  (let [[items set-items] (react/useState  (map clj->js entities))
        [getLabelProps getMenuProps getComboboxProps getToggleButtonProps getInputProps getItemProps isOpen highlightedIndex selectItem selectedItem setInputValue]
        (as-> (useCombobox (clj->js {:items items
                                     :defaultHighlightedIndex 0
                                     :defaultSelectedItem value
                                     :onInputValueChange (fn [input]
                                                           (if entities-by-id
                                                             (do 
                                                               (->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} )
                                                                    clj->js
                                                                    (take 10)
                                                                    (set-items)))

                                                             (set-items (map clj->js (take 10 (filter (fn [x] (str/includes? (or (some-> (entity->text x) str/lower-case) "")
                                                                                                                            (or (some-> (aget input "inputValue") str/lower-case) "")))
                                                                                                     entities))))))
                                     :stateReducer state-reducer
                                     :onSelectedItemChange (fn [z]
                                                             (when on-change
                                                               (on-change (js->clj (aget z "selectedItem") :keywordize-keys true))))})) $
          (map #(aget $ %) ["getLabelProps" "getMenuProps" "getComboboxProps" "getToggleButtonProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))]
        [:<>
         [:div.typeahead (assoc (js->clj (getComboboxProps))
                                :style style)
          (if selectedItem
            ^{:key "typeahead"} [:div.input (assoc (js->clj (getInputProps #js {:disabled (if disabled
                                                                                            "disabled"
                                                                                            "")}))
                                                   :on-key-up (fn [e]
                                                                (when (= 8 (aget e "keyCode" ))
                                                                  (selectItem nil)
                                                                  (setInputValue nil)
                                                                  (when on-change 
                                                                    (on-change nil))))
                                                   :class class
                                                   :tab-index "0")
                                 [:div.control
                                  [:div.tags.has-addons
                                   [:span.tag (entity->text (js->clj selectedItem :keywordize-keys true))]
                                   (when name 
                                     [:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}])
                                   (when-not disabled
                                     [:a.tag.is-delete {:on-click (fn []
                                                                    (setInputValue nil)
                                                                    (selectItem nil)
                                                                    (when on-change 
                                                                      (on-change nil)))}])]]]
            ^{:key "typeahead"} [:input.input (js->clj
                                               (getInputProps #js {:disabled (if disabled
                                                                               "disabled"
                                                                               "")
                                                                   :autoFocus (if auto-focus
                                                                                "autoFocus"
                                                                                "")}))])
          [:div {:class (if (and isOpen (seq items)) "typeahead-menu")}
           [:ul (js->clj (getMenuProps))
            (if (and isOpen (seq items))
              (for [[index item] (map vector (range) (js->clj items :keywordize-keys true))]
                ^{:key item}
                [:li.typeahead-suggestion (assoc (js->clj (getItemProps #js {:item item :index index}))
                                                 :class (if (= index highlightedIndex)
                                                          "typeahead-highlighted"))
                 (entity->text item)]))]]]]))

(defn typeahead-v3 [{:keys [class disabled entities entity->text entities-by-id entity-index on-change value ] :as props}]
  [:div 
   [:f> typeahead-v3-internal props]])
