(ns auto-ap.search
  (:require [config.core :refer [env]])
  (:import
   (java.nio.file Paths)
   (org.apache.lucene.analysis.standard StandardAnalyzer)
   (org.apache.lucene.document Document Field$Store StoredField StringField TextField)
   (org.apache.lucene.index DirectoryReader IndexWriter IndexWriterConfig Term)
   (org.apache.lucene.queryparser.classic QueryParser)
   (org.apache.lucene.search BooleanClause$Occur BooleanQuery$Builder IndexSearcher PhraseQuery$Builder Query TermQuery)
   (org.apache.lucene.store FSDirectory)))

(defn full-index-query [results index-name]
  (let [directory           (FSDirectory/open (Paths/get (java.net.URI. (str "file:///tmp/search/" (:dd-env env) "/"  index-name))))
        analyzer            (StandardAnalyzer.)
        index-writer-config (IndexWriterConfig. analyzer)
        index-writer        (IndexWriter. directory index-writer-config)]
    (.deleteAll index-writer)
    (try 
      (doseq [{:keys [text id] :as x} results
              :let      [doc (doto 
                                 (Document.)
                               (.add (TextField. "name" text Field$Store/YES))
                               (.add (StoredField. "id" (long id))))]]
        (doseq [k (filter (complement #{:text :id}) (keys x))]
          (.add doc (StringField. (name k) (str (get x k)) Field$Store/YES)))
        (.addDocument index-writer doc))
      (finally 
        (.close index-writer)))))

(defn make-query [n]
  (let [
        text-query   (when (:q n)
                       (.parse (QueryParser. "name" (StandardAnalyzer.)) (:q n)))
        text-query-exact (when (:q-exact n)
                       (.build (doto (PhraseQuery$Builder. )
                                 (.add (Term. "name" (:q-exact n)) 0))))
        full-query (BooleanQuery$Builder.)
        ]
    (when text-query 
      (.add full-query text-query BooleanClause$Occur/MUST))
    (when text-query-exact
      (.add full-query text-query-exact BooleanClause$Occur/MUST))
    (doseq [[k v] (dissoc n :q :q-exact)]
      (if (instance? Query v)
        (.add full-query v BooleanClause$Occur/MUST)
        (.add full-query (TermQuery. (Term. (name k) (str v))) BooleanClause$Occur/MUST)))
    (.build full-query)))

(defn search
  ([n index-name]
   (search n index-name []))
  ([n index-name other-keys]
   (let [directory      (FSDirectory/open (Paths/get (java.net.URI. (str "file:///tmp/search/" (:dd-env env) "/"  index-name))))
         index-reader   (DirectoryReader/open directory)
         index-searcher (IndexSearcher. index-reader)]
     (for [x (seq (.scoreDocs (.search index-searcher (make-query n) 10)))]
       (into 
         [(Long/parseLong (.get (.doc index-searcher (.-doc x)) "id"))
          (.get (.doc index-searcher (.-doc x)) "name")
          (.-score x)]
         (map (fn [o]
                (.get (.doc index-searcher (.-doc x)) o))
              other-keys)))))
  )

(defn search-ids [n index-name]
  (let [directory      (FSDirectory/open (Paths/get (java.net.URI. (str "file:///tmp/search/" (:dd-env env) "/"  index-name))))
        index-reader   (DirectoryReader/open directory)
        index-searcher (IndexSearcher. index-reader)]
    (for [x (seq (.scoreDocs (.search index-searcher (make-query n) 100)))]
      (Long/parseLong (.get (.doc index-searcher (.-doc x)) "id")))))
