;; gnus configuration -*- lexical-binding: t -*-

;;; Feature switching vars
(defvar jao-gnus-use-local-imap nil)
(defvar jao-gnus-use-leafnode nil)
(defvar jao-gnus-use-gandi-imap nil)
(defvar jao-gnus-use-pm-imap nil)
(defvar jao-gnus-use-gmane nil)
(defvar jao-gnus-use-nnml nil)
(defvar jao-gnus-use-maildirs nil)
(defvar jao-notmuch-enabled nil)

;;; Startup and kill
(setq gnus-interactive-exit t)

(defun jao-quit-gnus () (gnus-group-exit) t)

(add-hook 'kill-emacs-query-functions #'jao-quit-gnus)

;;; Directories
(defun jao-gnus-dir (dir)
  (expand-file-name dir gnus-home-directory))

(setq smtpmail-queue-dir (jao-gnus-dir "Mail/queued-mail/"))

(setq mail-source-directory (jao-gnus-dir "Mail/")
      message-directory (jao-gnus-dir "Mail/"))

(setq gnus-default-directory (expand-file-name "~")
      gnus-startup-file (jao-gnus-dir "newsrc")
      gnus-agent-directory (jao-gnus-dir "News/agent")
      gnus-home-score-file (jao-gnus-dir "scores")
      gnus-article-save-directory (jao-gnus-dir "saved/")
      nntp-authinfo-file (jao-gnus-dir "authinfo")
      nnmail-message-id-cache-file (jao-gnus-dir "nnmail-cache")
      nndraft-directory (jao-gnus-dir "drafts")
      nnrss-directory (jao-gnus-dir "rss"))

;;; Verbosity
(setq gnus-verbose 4)

;;; Looks
;;;; geometry
(defvar jao-gnus-use-three-panes t)
(defvar jao-gnus-groups-width 50)
(defvar jao-gnus-wide-width 190)

(setq gnus-use-trees nil
      gnus-generate-tree-function 'gnus-generate-horizontal-tree
      gnus-tree-minimize-window nil)

(when jao-gnus-use-three-panes

  ;; (dolist (m '(calendar-mode org-agenda-mode gnus-group-mode))
  ;;   (add-to-list 'display-buffer-alist `((major-mode . ,m) (dedicated t))))

  (let ((side-bar '(vertical 1.0
                             ("inbox.org" 0.4)
                             ("*Org Agenda*" 1.0)
                             ("*Calendar*" 9)))
        (wide-len jao-gnus-wide-width)
        (groups-len jao-gnus-groups-width)
        (summary-len (- jao-gnus-wide-width jao-gnus-groups-width)))
    (gnus-add-configuration
     `(article
       (horizontal 1.0
                   (vertical ,groups-len (group 1.0))
                   (vertical ,summary-len
                             (summary 0.25 point)
                             (article 1.0))
                   ,side-bar)))

    (gnus-add-configuration
     `(group (horizontal 1.0 (group ,wide-len point) ,side-bar)))

    (gnus-add-configuration
     `(message (horizontal 1.0 (message ,wide-len point) ,side-bar)))

    (gnus-add-configuration
     `(reply-yank (horizontal 1.0 (message ,wide-len point) ,side-bar)))

    (gnus-add-configuration
     `(summary
       (horizontal 1.0
                   (vertical ,groups-len (group 1.0))
                   (vertical ,summary-len (summary 1.0 point))
                   ,side-bar)))

    (gnus-add-configuration
     `(reply
       (horizontal 1.0
                   (message ,(- wide-len 100) point)
                   (article 100)
                   ,side-bar)))))

;;;; no blue icon
(advice-add 'gnus-mode-line-buffer-identification :override #'identity)
(setq gnus-mode-line-image-cache nil)

;;; Search
(setq gnus-search-use-parsed-queries nil
      gnus-search-notmuch-raw-queries-p nil
      gnus-permanently-visible-groups "^nnselect:.*"
      gnus-search-ignored-newsgroups "nndraft.*\\|nnselect.*")

(with-eval-after-load "gnus-search"
  (defclass gnus-search-recoll (gnus-search-indexed)
    ((separator :type string :initform ".")
     (program :initform "recoll")
     (raw-queries-p :initform t)))

  (cl-defmethod gnus-search-indexed-extract ((_engine gnus-search-recoll))
    (prog1 (and (looking-at "^file://\\(.+\\)$") (list (match-string 1) 100))
      (forward-line 1)))

  (cl-defmethod gnus-search-transform-expression ((_engine gnus-search-recoll)
                                                  expr)
    expr)

  (cl-defmethod gnus-search-indexed-search-command ((engine gnus-search-recoll)
                                                    (qstring string)
                                                    _query
                                                    &optional groups)
    (let* ((subdir (slot-value engine 'remove-prefix))
           (sep (slot-value engine 'separator))
           (gdirs (mapcar (lambda (g)
                            (let ((g (gnus-group-short-name g)))
                              (replace-regexp-in-string "\\." sep g)))
                          (or groups
                              (and (not (string= "" subdir)) (list subdir)))))
           (dirsq (and gdirs
                       (concat "("
                               (mapconcat (lambda (d) (format "dir:%s" d))
                                          gdirs " OR ")
                               ")")))
           (q (concat "mime:message " dirsq " (" qstring ")")))
      ;; (message "query is: %s" q)
      `("-b" "-t" "-q" ,q))))

;; (add-to-list 'gnus-parameters '("^nnselect:.*" (nnselect-rescan . t)))

;;; News server
(defvar jao-gnus-leafnode-spool "/var/spool/news/")
(setq gnus-select-method
      (cond
       (jao-gnus-use-leafnode
        `(nntp "localhost"
               (gnus-search-engine gnus-search-recoll
                                   (remove-prefix ,jao-gnus-leafnode-spool)
                                   (separator "/"))))
       (jao-gnus-use-gmane '(nntp "news.gmane.io"))
       (t '(nnnil ""))))

(setq gnus-secondary-select-methods '())

(setq nnheader-read-timeout 0.02
      gnus-save-newsrc-file nil) ; .newsrc only needed by other newsreaders

;; leafnode articles group parameters
(defvar jao-gnus-image-groups '("xkcd"))

(defvar jao-gnus-leafnode-group-params
  `((,(format "gwene\\..*%s.*" (regexp-opt jao-gnus-image-groups))
     (mm-html-inhibit-images nil)
     (mm-html-blocked-images nil))
    ("\\(gmane\\|gwene\\)\\..*"
     (jao-gnus--archiving-group "nnml:feeds.trove")
     (posting-style (address "jao@gnu.org")))))

(when jao-gnus-use-leafnode
  (dolist (p jao-gnus-leafnode-group-params)
    (add-to-list 'gnus-parameters p t)))

;;; Mail
;;;; nnmail
(setq nnmail-treat-duplicates 'delete
      nnmail-scan-directory-mail-source-once nil
      nnmail-cache-accepted-message-ids t
      nnmail-message-id-cache-length 100000
      nnmail-split-fancy-with-parent-ignore-groups nil
      nnmail-use-long-file-names t
      nnmail-crosspost t
      nnmail-resplit-incoming t
      nnmail-mail-splitting-decodes t
      nnmail-split-methods 'nnmail-split-fancy)

;;;; nnml
(setq mail-sources
      (when jao-gnus-use-nnml ;; (and jao-gnus-use-nnml (not jao-notmuch-enabled))
        (let ((fix (regexp-opt '("feeds" "local" "trash" "spam"))))
          (mapcar (lambda (d) `(maildir :path ,(concat d "/")))
                  (directory-files "~/var/mail/gnus/" t
                                   (concat "[^\\.]+\\.[^\\.]+\\|" fix)))))
      gnus-message-archive-group nil
      nnml-get-new-mail t
      nnml-directory message-directory)

(when jao-gnus-use-nnml
  (add-to-list
   'gnus-secondary-select-methods
   `(nnml "" (gnus-search-engine gnus-search-recoll
                                 (remove-prefix ,(jao-gnus-dir "Mail/"))))))

(defvar jao-gnus-nnml-group-params
  `(("nnml:\\(local\\|trash\\|spam\\)"
     (auto-expire . t)
     (total-expire . t)
     (expiry-wait . 1)
     (expiry-target . delete))
    ("nnml:jao\\..*"
     (posting-style ("Gcc" "nnml:jao.trove")) ;; ("Bcc" "proton@jao.io")
     (jao-gnus--trash-group "nnml:trash")
     (jao-gnus--spam-group "nnml:spam")
     (jao-gnus--archiving-group "nnml:jao.trove")
     (gcc-self . t))
    ("nnml:jao\\.hacking"
     (posting-style ("Gcc" "nnml:jao.hacking")
                    (address "jao@gnu.org"))) ;; ("Bcc" "hacking@jao.io")
    ("nnml:jao\\.drivel"
     (auto-expire . t)
     (total-expire . t)
     (expiry-wait . 3)
     (expiry-target . delete))
    ("nnml:bigml\\..*"
     (gcc-self . nil)
     (auto-expire . t)
     (total-expire . t)
     (expiry-wait . 3)
     (expiry-target . delete)
     (posting-style (address "jao@bigml.com"))
     (jao-gnus--trash-group "nnml:trash")
     (jao-gnus--spam-group "nnml:spam")
     (jao-gnus--archiving-group "nnml:bigml.trove"))
    ("nnml:bigml\\.inbox"
     (gcc-self . t)
     (auto-expire . t)
     (total-expire . t)
     (expiry-wait . 7)
     (expiry-target . "nnml:bigml.trove"))
    ("nnml:bigml\\.alba"
     (gcc-self . t)
     (auto-expire . nil)
     (total-expire . nil)
     (expiry-target . nil)
     (expiry-wait . nil))
    ("nnml:bigml\\.trove"
     (auto-expire . t)
     (total-expire . t)
     (expiry-target . delete)
     (expiry-wait . 365))
    ("nnml:feeds\\.\\(.*\\)"
     (posting-style ("Gcc" "nnml:feeds.trove")
                    (address "jao@gnu.org"))
     (auto-expire . t)
     (total-expire . t)
     (expiry-wait . 7)
     (expiry-target . delete)
     (comment . "feeds.\\1")
     (jao-gnus--archiving-group "nnml:feeds.trove"))
    ("nnml:feeds\\.\\(news\\|emacs-\\(bugs\\|diffs|\\github\\)\\)$"
     (expiry-wait . 2))
    ("nnml:feeds\\.trove"
     (auto-expire . nil)
     (total-expire . nil))
    ("nnml:feeds\\.fun"
     (mm-html-inhibit-images nil)
     (mm-html-blocked-images nil))))

(when jao-gnus-use-nnml
  (dolist (p jao-gnus-nnml-group-params)
    (add-to-list 'gnus-parameters p t)))

;;;; imap
(setq nnimap-quirks nil)

(when jao-gnus-use-local-imap
  (add-to-list 'gnus-secondary-select-methods
               `(nnimap "" (nnimap-address "localhost"))))

(when jao-gnus-use-pm-imap
  (add-to-list 'gnus-secondary-select-methods
               '(nnimap "pm"
                        (nnimap-address "127.0.0.1")
                        (nnimap-stream network)
                        (nnimap-server-port 1143))))

(when jao-gnus-use-gandi-imap
  (add-to-list 'gnus-secondary-select-methods
               '(nnimap "gandi" (nnimap-address "mail.gandi.net"))))

;;; Groups
(setq gnus-group-line-format
      " %m%S%p%3y%P%* %~(pad-right 30)G %B\n"
      ;; " %m%S%p%P:%~(pad-right 35)c %3y %B\n"
      ;; " %m%S%p%3y%P%* %~(pad-right 30)C %B\n"
      gnus-topic-line-format "%i[ %(%{%n%}%) -- %A ]%v\n"
      gnus-group-uncollapsed-levels 2
      gnus-auto-select-subject 'unread
      gnus-large-newsgroup 2000)

(add-hook 'gnus-select-group-hook 'gnus-group-set-timestamp)
(add-hook 'gnus-group-mode-hook 'gnus-topic-mode)

;;; Summary
;;;; configuration
(setq gnus-face-1 'jao-gnus-face-tree
      gnus-show-threads t
      gnus-thread-hide-subtree t
      gnus-summary-make-false-root 'adopt
      gnus-summary-gather-subject-limit 120
      gnus-sort-gathered-threads-function 'gnus-thread-sort-by-date
      gnus-thread-sort-functions '(gnus-thread-sort-by-date))

(setq gnus-summary-ignore-duplicates t
      gnus-suppress-duplicates t
      ;; gnus-summary-ignored-from-addresses jao-mails-regexp
      gnus-process-mark-toggle t
      gnus-refer-thread-use-search t
      gnus-auto-select-next 'almost-quietly)

;;;; search on enter nnselect
(defun jao-gnus--maybe-reselect (&rest _i)
  (when (string-match-p "^nnselect" (or (gnus-group-name-at-point) ""))
    (save-excursion (gnus-group-get-new-news-this-group))))

(advice-add 'gnus-group-select-group :before #'jao-gnus--maybe-reselect)

;;;; summary line
(setq gnus-not-empty-thread-mark ?↓) ; ↓) ?·
(setq jao-gnus--summary-line-fmt
      (concat "%%U %%*%%R %%uj "
              "[ %%~(max-right 23)~(pad-right 23)uf "
              " %%I%%~(pad-left 2)t ] %%s"
              "%%-%s="
              "%%~(max-right 8)~(pad-left 8)&user-date;"
              "\n"))

(defun jao-gnus--set-summary-line (&optional w)
  (let* ((d (if jao-gnus-use-three-panes (+ jao-gnus-groups-width 11) 12))
         (w (- (or w (window-width)) d)))
    (setq gnus-summary-line-format (format jao-gnus--summary-line-fmt w))))

(add-hook 'gnus-select-group-hook 'jao-gnus--set-summary-line)
;; (jao-gnus--set-summary-line 187)

(add-to-list 'nnmail-extra-headers 'Cc)
(add-to-list 'nnmail-extra-headers 'BCc)

(use-package gnus-sum
  :config
  (add-to-list 'gnus-extra-headers 'Cc)
  (add-to-list 'gnus-extra-headers 'BCc))


(defun gnus-user-format-function-j (headers)
  (let ((to (gnus-extra-header 'To headers)))
    (if (string-match jao-mails-regexp to)
        (if (string-match "," to) "¬" "»") ;; "~" "=")
      (if (or (string-match jao-mails-regexp
                            (gnus-extra-header 'Cc headers))
              (string-match jao-mails-regexp
                            (gnus-extra-header 'BCc headers)))
          "¬" ;; "~"
        " "))))

(defconst jao-gnus--news-rx
  (concat (regexp-opt '("ElDiaro.es "
                        "ElDiario.es - ElDiario.es: "
                        "The Guardian: "
                        "Aeon | a world of ideas: "
                        "Planet Debian: "))
          "\\|The Conversation – Articles (.+): "
          "\\|unofficial mirror of [^:]+: "
          "\\|[gq].+ updates on arXiv.org: "))

(defun gnus-user-format-function-f (headers)
  (let* ((from (gnus-header-from headers))
         (from (gnus-summary-extract-address-component from))
         (from (replace-regexp-in-string jao-gnus--news-rx "" from)))
    from))

(setq gnus-user-date-format-alist
      '(((gnus-seconds-today) . "%H:%M")
        ((+ 86400 (gnus-seconds-today)) . "'%H:%M")
        ;; (604800 . "%a %H:%M") ;; that's one week
        ((gnus-seconds-month) . "%a %d")
        ((gnus-seconds-year) . "%b %d")
        (t . "%b '%y")))

;;;; moving messages around
(defvar-local jao-gnus--spam-group nil)
(defvar-local jao-gnus--archiving-group nil)
(defvar-local jao-gnus--archive-as-copy-p nil)

(defvar jao-gnus--last-move nil)
(defun jao-gnus-move-hook (a headers c to d)
  (setq jao-gnus--last-move (cons to (mail-header-id headers))))
(defun jao-gnus-goto-last-moved ()
  (interactive)
  (when jao-gnus--last-move
    (when (eq major-mode 'gnus-summary-mode) (gnus-summary-exit))
    (gnus-group-goto-group (car jao-gnus--last-move))
    (gnus-group-select-group)
    (gnus-summary-goto-article (cdr jao-gnus--last-move) nil t)))
(add-hook 'gnus-summary-article-move-hook 'jao-gnus-move-hook)

(defun jao-gnus-archive (follow)
  (interactive "P")
  (if jao-gnus--archiving-group
      (progn
        (if (or jao-gnus--archive-as-copy-p
                (not (gnus-check-backend-function
                      'request-move-article gnus-newsgroup-name)))
            (gnus-summary-copy-article nil jao-gnus--archiving-group)
          (gnus-summary-move-article nil jao-gnus--archiving-group))
        (when follow (jao-gnus-goto-last-moved)))
    (gnus-summary-mark-as-read)
    (gnus-summary-delete-article)))

(defun jao-gnus-archive-tickingly ()
  (interactive)
  (gnus-summary-tick-article)
  (jao-gnus-archive)
  (when jao-gnus--archive-as-copy-p
    (gnus-summary-mark-as-read)))

(defun jao-gnus-show-tickled ()
  (interactive)
  (gnus-summary-limit-to-marks "!"))

(make-variable-buffer-local
 (defvar jao-gnus--trash-group nil))

(defun jao-gnus-trash ()
  (interactive)
  (gnus-summary-mark-as-read)
  (if jao-gnus--trash-group
      (gnus-summary-move-article nil jao-gnus--trash-group)
    (gnus-summary-delete-article)))

(defun jao-gnus-move-to-spam ()
  (interactive)
  (gnus-summary-mark-as-read)
  (gnus-summary-move-article nil jao-gnus--spam-group))

(define-key gnus-summary-mode-map "Ba" 'jao-gnus-archive)
(define-key gnus-summary-mode-map "BA" 'jao-gnus-archive-tickingly)
(define-key gnus-summary-mode-map "Bl" 'jao-gnus-goto-last-moved)

(define-key gnus-summary-mode-map (kbd "B DEL") 'jao-gnus-trash)
(define-key gnus-summary-mode-map (kbd "B <backspace>") 'jao-gnus-trash)
(define-key gnus-summary-mode-map "Bs" 'jao-gnus-move-to-spam)
(define-key gnus-summary-mode-map "/!" 'jao-gnus-show-tickled)
(define-key gnus-summary-mode-map [f7] 'gnus-summary-force-verify-and-decrypt)

;;;; saving emails
(setq gnus-default-article-saver 'gnus-summary-save-article-mail)
(defvar jao-gnus-file-save-directory (expand-file-name "~/tmp"))
(defun jao-gnus-file-save (newsgroup headers &optional last-file)
  (expand-file-name (format "%s.eml" (mail-header-subject headers))
                    jao-gnus-file-save-directory))
(setq gnus-mail-save-name 'jao-gnus-file-save)

;;;; arXiv capture
(use-package org-capture
  :config
  (add-to-list 'org-capture-templates
               '("X" "arXiv" entry (file "notes/physics/arxiv.org")
                 "* %:subject\n  %i" :immediate-finish t)
               t)
  (org-capture-upgrade-templates org-capture-templates))

(defun jao-gnus-arXiv-capture ()
  (interactive)
  (gnus-summary-select-article-buffer)
  (gnus-article-goto-part 0)
  (forward-paragraph)
  (setq-local transient-mark-mode 'lambda)
  (set-mark (point))
  (goto-char (point-max))
  (org-capture nil "X"))

;;; Article
;;;; config, headers
(setq mail-source-delete-incoming t)
(setq gnus-gcc-mark-as-read t)
(setq gnus-treat-display-smileys nil)
(setq gnus-treat-fill-long-lines nil)
(setq gnus-treat-fill-article 120)
(setq gnus-treat-fold-headers nil)
(setq gnus-treat-strip-leading-blank-lines t)
(setq gnus-article-auto-eval-lisp-snippets nil)
(setq gnus-posting-styles '((".*" (name "Jose A. Ortega Ruiz"))))
(setq gnus-single-article-buffer nil)
(setq gnus-article-update-lapsed-header 60)
(setq gnus-article-update-date-headers 60)

(with-eval-after-load "gnus-art"
  (setq gnus-visible-headers
        (concat
         gnus-visible-headers
         "\\|^List-[iI][Dd]:\\|^X-Newsreader:\\|^X-Mailer:"
         "\\|^User-Agent:\\|^X-User-Agent:\\|^X-RSS-Feed:")))

;;;; html and images
(setq  gnus-button-url 'browse-url-generic
       gnus-inhibit-images t
       mm-discouraged-alternatives nil ;; '("text/html" "text/richtext")
       mm-inline-large-images 'resize)

(defvar-local jao-gnus--images nil)

(defun jao-gnus--init-images ()
  (with-current-buffer gnus-article-buffer
    (setq jao-gnus--images nil)))

(add-hook 'gnus-select-article-hook #'jao-gnus--init-images)

(defun jao-gnus-browse-html ()
  (interactive)
  (let ((browse-url-browser-function jao-browse-url-external-function)
        (browse-url-handlers nil)
        (browse-url-default-handlers nil))
    (gnus-article-browse-html-article)))

(defun jao-gnus-show-images ()
  (interactive)
  (if window-system
      (save-window-excursion
        (gnus-summary-select-article-buffer)
        (save-excursion
          (if (and jao-afio-use-w3m (fboundp 'w3m-toggle-inline-images))
              (w3m-toggle-inline-images)
            (setq jao-gnus--images (not jao-gnus--images))
            (if jao-gnus--images
                (gnus-article-show-images)
              (gnus-article-remove-images)))))
    (jao-gnus-browse-html)))

;;;; remove html in From:
(require 'shr)
(defvar jao-gnus--from-rx
  (concat "From: \\\"?\\( *" jao-gnus--news-rx "\\)"))

(defun jao-gnus-format-from ()
  (save-excursion
    (goto-char (point-min))
    (when (re-search-forward jao-gnus--from-rx nil t)
      (replace-match "" nil nil nil 1))))

(add-hook 'gnus-part-display-hook 'jao-gnus-format-from)

;;;; follow links and enclosures
(defun jao-gnus-follow-link (&optional external)
  (interactive "P")
  (when (eq major-mode 'gnus-summary-mode)
    (gnus-summary-select-article-buffer))
  (save-excursion
    (goto-char (point-min))
    (when (or (search-forward-regexp "^Via: h" nil t)
              (search-forward-regexp "^URL: h" nil t)
              (and (search-forward-regexp "^Link$" nil t)
                   (not (beginning-of-line))))
      (if external
          (jao-browse-with-external-browser)
        (browse-url (jao-url-around-point))))))

(defun jao-gnus-open-enclosure ()
  (interactive)
  (save-window-excursion
    (gnus-summary-select-article-buffer)
    (save-excursion
      (goto-char (point-min))
      (let ((offset (or (and (search-forward-regexp "^Enclosure: " nil t) 2)
                        (and (search-forward-regexp "^Enclosure$" nil t) -2))))
        (when offset (forward-char offset))
        (if-let ((url (jao-url-around-point)))
            (jao-mpc-add-or-play-url url)
          (error "No enclosure found"))))))

;;;; delayed messages
(require 'gnus-util)
(gnus-delay-initialize)
(setq gnus-delay-default-delay "3h")
(eval-after-load "message"
  '(setq message-draft-headers (remove 'Date message-draft-headers)))

;;; Add-ons
;;;; notifications
;;;;; minibuffer
(defvar jao-gnus-tracked-groups
  (let ((feeds (thread-first
                 (directory-files mail-source-directory nil "feeds\\.[^e]")
                 (seq-difference '("feeds.trove")))))
    `(("nnml:bigml\\.inbox" "B" jao-themes-f00)
      ("nnml:bigml\\.alba" "A" jao-themes-f00)
      ("nnml:bigml\\.bugs" "b" jao-themes-error)
      ("nnml:bigml\\.support" "S" default)
      ("nnml:jao\\.\\(inbox\\|trove\\)" "I" jao-themes-f01)
      ("nnml:bigml\\.[^ibs]" "W" jao-themes-dimm)
      ("nnml:jao.hacking" "H" jao-themes-dimm)
      ("nnml:jao.[^isth]" "J" jao-themes-dimm)
      (,(format "^nnml:%s" (regexp-opt feeds)) "F" jao-themes-dimm)
      ("feeds\\.e" "E" jao-themes-dimm)
      ("nnml:local" "l" jao-themes-dimm))))

(defun jao-gnus--unread-counts ()
  (seq-reduce (lambda (r g)
                (let ((n (gnus-group-unread (car g))))
                  (if (and (numberp n) (> n 0))
                      (prog1 (cons (cons (car g) n) r)
                        (gnus-message 7 "%s in %s" n g))
                    r)))
              gnus-newsrc-alist
              ()))

(defun jao-gnus--unread-label (counts rx label face)
  (let ((n (seq-reduce (lambda (n c)
                         (if (string-match-p rx (car c)) (+ n (cdr c)) n))
                       counts
                       0)))
    (when (> n 0) `(:propertize ,(format "%s%d " label n) face ,face))))

(defvar jao-gnus--notify-strs ())

(defun jao-gnus--notify-strs ()
  (let ((counts (jao-gnus--unread-counts)))
    (seq-filter #'identity
                (seq-map (lambda (args)
                           (apply 'jao-gnus--unread-label counts args))
                         jao-gnus-tracked-groups))))

(defun jao-gnus--notify ()
  (setq jao-gnus--notify-strs (jao-gnus--notify-strs))
  (jao-minibuffer-refresh))

(with-eval-after-load "jao-minibuffer"
  (jao-minibuffer-add-variable 'jao-gnus--notify-strs -20))

(add-hook 'gnus-started-hook #'jao-gnus--notify)
(add-hook 'gnus-summary-exit-hook #'jao-gnus--notify)
(add-hook 'gnus-after-getting-new-news-hook #'jao-gnus--notify)

;;;;; daemon config
(setq mail-user-agent 'gnus-user-agent)
(setq gnus-asynchronous t)
(setq gnus-use-article-prefetch nil)
(setq gnus-save-killed-list nil)
(setq gnus-check-new-newsgroups nil)

(require 'gnus-demon)

(defun jao-gnus--scan ()
  (let ((inhibit-message t))
    (gnus-demon-scan-news)
    (jao-gnus--notify)))

(gnus-demon-add-handler 'jao-gnus--scan 5 1)

;;;;; agenda updates
(add-hook 'gnus-summary-exit-hook #'org-agenda-list)


;;;; open mail file in gnus
(defun jao-gnus-file-to-group (file &optional maildir newsdir m-server n-server)
  "Compute the Gnus group name from the given file name.
  IN: /home/jao/.emacs.d/gnus/Mail/jao.trove/32, /home/jao/.emacs.d/gnus/Mail/
  OUT: nnml:jao.trove "
  (let* ((maildir (or maildir message-directory))
         (newsdir (or newsdir jao-gnus-leafnode-spool))
         (m-server (or m-server "nnml"))
         (n-server (or n-server "nntp+localhost"))
         (nntp (and newsdir (string-match-p newsdir file)))
         (g (directory-file-name (file-name-directory file)))
         (g (replace-regexp-in-string (file-name-as-directory maildir) "" g))
         (g (replace-regexp-in-string (file-name-as-directory newsdir) "" g))
         (g (cond (nntp (concat n-server ":" g))
                  ((file-name-directory g)
                   (replace-regexp-in-string "^\\([^/]+\\)/"
                                             (concat m-server ":\\1/")
                                             (file-name-directory g) t))
                  (t (concat m-server ":" g))))
         (g (replace-regexp-in-string "/" "." g))
         (g (replace-regexp-in-string "[/.]$" "" g)))
    (cond ((string-match ":$" g) (concat g "inbox"))
          (nntp g)
          (t (replace-regexp-in-string ":\\." ":" g)))))

(defun jao-gnus-goto-file (filename &optional _page)
  (let ((group (jao-gnus-file-to-group filename))
        (id (file-name-nondirectory filename)))
    (if (and group id)
        (org-gnus-follow-link group id)
      (message "Couldn't get relevant info for switching to Gnus."))))

;;;; afio
(defun jao-gnus--on-afio-switch ()
  (when (derived-mode-p 'gnus-group-mode)
    (let ((no (or (gnus-group-unread (gnus-group-group-name)) 0)))
      (unless (> no 0) (gnus-group-first-unread-group)))))

(add-hook 'jao-afio-switch-hook #'jao-gnus--on-afio-switch)

;;;; gnus-icalendar
(require 'ol-gnus)
(use-package gnus-icalendar
  :demand t
  :init (setq gnus-icalendar-org-capture-file
              (expand-file-name "inbox.org" org-directory)
              gnus-icalendar-org-capture-headline '("Appointments"))
  :config (gnus-icalendar-org-setup))

;;;; bbdb
(with-eval-after-load "bbdb"
  ;; (bbdb-initialize 'gnus 'message 'pgp)
  (bbdb-mua-auto-update-init 'gnus)
  (with-eval-after-load "gnus-sum"
    (define-key gnus-summary-mode-map ":" 'bbdb-mua-annotate-sender)
    (define-key gnus-summary-mode-map ";" 'bbdb-mua-annotate-recipients)))

;;;; randomsig
(with-eval-after-load "randomsig"
  (with-eval-after-load "gnus-sum"
    (define-key gnus-summary-save-map "-" 'gnus/randomsig-summary-read-sig)))

;;;; recoll
(unless jao-notmuch-enabled
  (with-eval-after-load "org"
    (org-link-set-parameters "message" :follow #'jao-gnus-goto-file))
  (with-eval-after-load "consult-recoll"
    (add-to-list 'consult-recoll-open-fns
                 '("message/rfc822" . jao-gnus-goto-file))))
;;; Keyboard shortcuts
(define-key gnus-article-mode-map "i" 'jao-gnus-show-images)
(define-key gnus-summary-mode-map "i" 'jao-gnus-show-images)
(define-key gnus-article-mode-map "\M-g" 'jao-gnus-follow-link)
(define-key gnus-summary-mode-map "\M-g" 'jao-gnus-follow-link)
(define-key gnus-summary-mode-map "v" 'scroll-other-window)
(define-key gnus-summary-mode-map "V" 'scroll-other-window-down)
(define-key gnus-summary-mode-map "X" 'jao-gnus-arXiv-capture)
(define-key gnus-summary-mode-map "e" 'jao-gnus-open-enclosure)
(define-key gnus-summary-mode-map "\C-l" nil)
(define-key gnus-group-mode-map (kbd "A") 'nil)