;;;; Local search tools
(gptel-make-tool
 :name "search_local_db"
 :function (lambda (&rest args) (gptel--json-encode
                            (vconcat (apply #'my/gptel-wombag-search args)
                                     (apply #'my/gptel-elfeed-search args))))
 :description "Query the user's Elfeed and read-it-later databases containing RSS feed entries and saved articles respectively.  Return an array of feed entries, each of which is a JSON object containing the following fields:

\"title\": the title of the feed entry
\"feed-title\": the name of the feed
\"url\": A web link to the entry
\"date\": The date the entry was published, in YYYY-MM-DD format
\"content\": The content of the entry.  This is optional, not all entries have content."
 :args (list '(:name "query"
               :description "A space separated string of query terms.  For example, \"programming elixir beam\""
               :type "string")
             '(:name "isVideo"
               :description "A boolean indicating if the query is for videos."
               :type "boolean")
             '(:name "daterange"
               :description "A range of publication dates to search between, in the format YYYY-MM-DD--YYYY-MM-DD.
For example, \"2023-12-01--2024-01-10\"."
               :type "string"
               :optional t)
             '(:name "feed_title"
               :description "feed-title to restrict searches to."
               :type "string"
               :optional t))
 :category "local search")

(defun my/gptel-wombag-search (query-multiple _is-video &optional daterange feed-title)
  (let ((num 5))
    (thread-last
      (split-string query-multiple)
      (mapcan
       (lambda (query)
         (let* ((wombag-search-filter
                 (concat (format "#%d *%s " (max 2 (cl-decf num)) query)
                         (when daterange (format "@%s " daterange))
                         (when feed-title (format "^%s " feedtitle))))
                (filter (wombag-search-parse-filter
                         wombag-search-filter wombag-search-columns))
                (entries (wombag-db-get-entries filter wombag-search-columns)))
           (when entries
             (cl-loop for entry in entries
                      for id = (alist-get 'id entry)
                      collect
                      (nconc
                       (list :title (or (alist-get 'title entry) "NO TITLE")
                             :feed-title (or (alist-get 'domain_name entry) "")
                             :url (alist-get 'url entry)
                             :date (or (alist-get 'published_at entry)
                                       (alist-get 'created_at entry)))
                       (list :content
                             (if-let ((content (car
                                                (car-safe
                                                 (wombag-db-query `[:select content :from items :where (= id ,id)])))))
                                 (with-temp-buffer
                                   (insert content)
                                   (shr-render-region (point-min) (point-max))
                                   (buffer-substring-no-properties (point-min) (point-max)))))))))))
      (vconcat))))

(defun my/gptel-elfeed-search (query-multiple is-video &optional daterange feed-title)
  (let ((num 5))
    (thread-last
      (split-string query-multiple)
      (mapcan
       (lambda (query) 
         (let* ((elfeed-search-filter
                 (concat (format "#%d " (max 2 (cl-decf num))) query " "
                         (when daterange (format "@%s " daterange))
                         (when feed-title (format "=%s " feedtitle))
                         (when (and is-video (not (eq is-video :json-false))) "+youtube ")))
                (filter (elfeed-search-parse-filter elfeed-search-filter))
                (head (list nil)) (tail head) (count 0)
                (lexical-binding t)
                (search-func (byte-compile (elfeed-search-compile-filter filter))))
           (with-elfeed-db-visit (entry feed)
             (when (funcall search-func entry feed count)
               (setf (cdr tail) (list entry)
                     tail (cdr tail)
                     count (1+ count))))
           (when-let ((entries (cdr head)))
             (cl-loop
              for entry in entries
              for feed = (elfeed-entry-feed entry)
              collect
              (nconc
               (list :title (or (elfeed-meta entry :title) (elfeed-entry-title entry) "")
                     :feed-title (or (elfeed-meta feed :title) (elfeed-feed-title feed))
                     :url (elfeed-entry-link entry)
                     :date (format-time-string "%Y-%m-%d" (elfeed-entry-date entry)))
               (when-let* ((ref (elfeed-entry-content entry))
                           (html (elfeed-deref ref)))
                 (list :content
                       (with-temp-buffer
                         (insert html)
                         (shr-render-region (point-min) (point-max))
                         (buffer-substring-no-properties (point-min) (point-max)))))))))))
      (vconcat))))