;;; mail-settings.el --- Mail settings  -*- lexical-binding: t -*-

;;; Commentary:
;; https://kkatsuyuki.github.io/notmuch-conf/
;; https://opensource.com/article/20/1/organize-email-notmuch
;; https://notmuchmail.org/emacstips/
;; https://bostonenginerd.com/posts/notmuch-of-a-mail-setup-part-2-notmuch-and-emacs/

;; `mm-text-html-renderer'

;; `mailcap-parse-mailcaps' in mailcap.el uses a given path or the
;; value of environment variable MAILCAPS if set or either
;; ("~/.mailcap", "/etc/mailcap", "/usr/etc/mailcap",
;; "/usr/local/etc/mailcap") for *nix or ("~/.mailcap", "~/mail.cap",
;; "~/etc/mail.cap") for `mailcap-poor-system-types'.  parsed
;; mailcaps, see `mailcap-parsed-p', are in `mailcap-mime-data'.

;; `notmuch-show-view-part' `mm-inlined-types'
;; `notmuch-show-attachment-debug'

;;; Requirments:
;; (mu / mu4e)
;;   mu4e-maildirs-extension https://melpa.org/#/mu4e-maildirs-extension https://github.com/agpchil/mu4e-maildirs-extension https://www.djcbsoftware.nl/code/mu/mu4e/
;; (notmuch)

;;; Code:
(use-package message
  :defer t
  :config
  (set-face-attribute 'message-header-name nil :foreground "#4f97d7")
  (set-face-attribute 'message-header-subject nil :foreground "#bc6ec5")
  (set-face-attribute 'message-header-to nil :foreground "#c56ec3")
  (set-face-attribute 'message-header-cc nil :foreground "#c56ec3")
  (set-face-attribute 'message-header-other nil :foreground "#ce537a")
  (set-face-attribute 'message-cited-text nil :foreground "#9f8766")
  (set-face-attribute 'message-mml nil :foreground "#86dc2f")  ;; mime meta language
  ;; define in custom.el user-mail-address, user-full-name
  (setq message-directory "~/.local/share/mail/")                   ;; sent mails
  (setq message-auto-save-directory "~/.local/share/mail/drafts/")  ;; drafts
  (setq message-kill-buffer-on-exit t)  ;; after sending mail
  (setq mm-text-html-renderer 'shr))

(defgroup my-mu4e nil
  "My mu4e concept mapping"
  :prefix "my-mu4e-"
  :group 'my)

(defcustom my-mu4e-sync 'test
  "One of `test', `offlineimap' and `mbsync'.
see also `my-mu4e-offlineimap-command' and `my-mu4e-mbsync-command'.

`mu4e-get-mail-command' and `mu4e-change-filenames-when-moving' will
be set according to this variable."
  :group 'my-mu4e
  :type '(choice
    symbol
    (const :tag "test" test)
    (const :tag "offlineimap" offlineimap)
    (const :tag "mbsync" mbsync)))

(defcustom my-mu4e-offlineimap-command "offlineimap"
  "Oflineimap command used for mu4e if set in `my-mu4e-sync'."
  :group 'my-mu4e
  :type '(choice string (const :tag "offlineimap" "offlineimap")))

(defcustom my-mu4e-mbsync-command "mbsync"
  "mbsync command used for mu4e if set in `my-mu4e-sync'."
  :group 'my-mu4e
  :type '(choice string (const :tag "mbsync" "mbsync")))

(defcustom my-mu4e-bookmarks
  '((:name "Unread messages"
           :query "flag:unread AND NOT flag:trashed"
           :key ?u)
    (:name "Today's messages"
           :query "date:today..now"
           :key ?t)
    (:name "Last 7 days"
           :query "date:7d..now"
           :hide-unread t
           :key ?w)
    (:name "Messages with images"
           :query "mime:image/*"
           :key ?p))
  "Documention see `mu4e-bookmarks' in `mu4e-vars.el'.

:query same as \"mu find xxx\""
  :group 'my-mu4e
  :type '(repeat (plist)))

(defcustom my-mu4e-attachment-dir (expand-file-name "~/")
  "Documention see `mu4e-attachment-dir' in `mu4e-vars.el'."
  :group 'my-mu4e
  :type '(choice
          directory
          (const :tag "~/" "~/")
          (const :tag "~/Downloads" "~/Downloads"))
  :safe 'stringp)

(defcustom my-mu4e-contexts nil
  "Documention see `mu4e-contexts' in `mu4e-context.el'."
  :group 'my-mu4e
  :type '(list))

(defcustom my-mu4e-user-mailing-lists nil
  "Documention see `mu4e-user-mailing-lists' in `mu4e-lists.el'."
  :group 'my-mu4e
  :type '(repeat (cons string string)))

;; note `mu4e-headers-results-limit'
;; headers view
;;   [/] `mu4e-headers-search-narrow' - filter messages in current search query (x AND y)
;;   [\] `mu4e-headers-query-prev' - go back to previous search query
(use-package mu4e
  :delight
  (mu4e-main-mode "Mum")         ;; "\u01F15C\uFF4D"
  (mu4e-headers-mode "Muh")      ;; "\u01515C\uFF48" will be overwitten with `mu4e~headers-search-execute'
  (mu4e-view-mode "Muv")         ;; "\u01F15C\uFF56"
  (mu4e~update-mail-mode "Muu")  ;; "\u01F15C\uFF55"
  :commands mu4e
  :config
  ;; allow to display emoji in the subject lines of the emails
  (delete 'mu4e-headers-mode emojify-inhibit-major-modes)  ;; https://github.com/iqbalansari/emacs-emojify/issues/39

  ;; set `mu4e-get-mail-command' (runs mu index afterwards, by default key U in main or C-c C-u else)
  ;; and `mu4e-change-filenames-when-moving'
  ;; no default cond for custom settings
  (cond ((eq my-mu4e-sync 'test)
         "reset to saved (`custom-set-variables') or standard (if not saved) value"
         (custom-reevaluate-setting 'mu4e-get-mail-command)  ;; true for testing
         (custom-reevaluate-setting 'mu4e-change-filenames-when-moving))
        ((eq my-mu4e-sync 'offlineimap)
         (setq mu4e-get-mail-command my-mu4e-offlineimap-command)
         (setq mu4e-change-filenames-when-moving nil))  ;; nil better for offlineimap
        ((eq my-mu4e-sync 'mbsync)
         (setq mu4e-get-mail-command my-mu4e-mbsync-command)
         (setq mu4e-change-filenames-when-moving t)))  ;; t better for mbsync
  
  ;;; vars
  (setq mu4e-completing-read-function 'ivy-completing-read)
  (setq mu4e-confirm-quit nil)
  (setq mu4e-use-fancy-chars nil)  ;; use unicodes for fancy tags etc. But this does not means emojis are activated.
  (setq mu4e-bookmarks my-mu4e-bookmarks)
  (setq mu4e-attachment-dir my-mu4e-attachment-dir)

  ;;; headers
  ;;(setq mu4e-headers-results-limit 500) ;; to ignore the limit for one query press a C-u before invoking the search. consider narrowing the search instead of increasing the limit number
  (setq mu4e-headers-fields '((:human-date . 12)
                              (:flags . 6)
                              (:mailing-list . 10)
                              ;;(:from . 22)
                              (:from-or-to . 22)  ;; :from-or-to shows From: except when the e-mail was sent by the user (i.e., you) in that case it shows To: (prefixed by To, see also `mu4e-headers-from-or-to-prefix')
                              (:subject)))
  ;;(setq mu4e-headers-from-or-to-prefix '("" . "To "))  ;; prefix "To " for sent messages
  
  ;;; message
  ;;(setq mu4e-html2text-command ...)

  ;;; view
  (setq mu4e-view-show-images t)
  (setq mu4e-view-show-addresses t)
  (add-to-list 'mu4e-headers-actions '("view in browser" . mu4e-action-view-in-browser))
  (add-to-list 'mu4e-view-actions '("view in browser" . mu4e-action-view-in-browser))
  
  ;;; actions
  (setq mu4e-action-tags-header "X-Keywords")  ;; for e.g. gmail

  ;;; compose
  ;; use mu4e for e-mail in emacs
  (setq mail-user-agent 'mu4e-user-agent)  ;; for completion for To, CC, BSS fields
  ;; (setq mu4e-sent-messages-behavior 'delete)  ;; TODO: sent is automatic done via server? therefore delete?

  ;;; draft
  ;;(defalias 'mu4e-compose-signature 'message-signature)  ;; point to `message-signature'
  (setq mu4e-compose-signature-auto-include nil)  ;; see also `mu4e-compose-signature defined in contexts

  ;;; context
  ;; switch context with M-x mu4e-context-switch (force reload (if same context) with prefix-argument C-u)
  ;; see current context with M-x mu4e-context-current
  (setq mu4e-contexts my-mu4e-contexts)
  (defun my-mu4e-message-maildir-matches (msg rx)
    "helper to determine which account context to use based on the
folder in maildir the email (eg. ~/.mail/work) is located in."
    (when rx
      (if (listp rx)
	        ;; If rx is a list, try each one for a match
	        (or (mu4e-message-maildir-matches msg (car rx))
	            (mu4e-message-maildir-matches msg (cdr rx)))
	      ;; Not a list, check rx
	      (string-match rx (mu4e-message-field msg :maildir)))))
  ;; overwite in custom.el
  (defun my-mu4e-refile-folder-work (msg)
    "Function for chosing the refile folder for the work account.
MSG is a message p-list from mu4e."
    (cond
     ;; nil no refiling
     ))

  ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should
  ;; guess or ask the correct context, e.g.
  (setq mu4e-context-policy 'pick-first)  ;; start with the first (default) context; default is to ask-if-none (ask when there's no context yet, and none match)
  ;; (setq mu4e-compose-context-policy nil)  ;; compose with the current context is no context matches; default is to ask

  ;;; lists
  ;; The shortname (friendly) should a at most 8 characters, camel-case, see also `mu4e-mailing-list-patterns'
  (setq mu4e-user-mailing-lists my-mu4e-user-mailing-lists)

  ;;; org
  ;;(setq mu4e-org-link-query-in-headers-mode nil)  ;; point to message (default) rather then the query
  )

(use-package mu4e-maildirs-extension
  :after mu4e
  :config
  (setq mu4e-maildirs-extension-title
        (concat "  Maildirs\t(["
                (key-description mu4e-maildirs-extension-toggle-maildir-key)
                "] fold, [C-u "
                (key-description mu4e-maildirs-extension-toggle-maildir-key)
                "] fold w/ children, [RET]/[mouse-2] jump to, [C-u RET] jump to unread only)\n"))
  (setq mu4e-maildirs-extension-action-text
        (concat "\t* ["
                (key-description mu4e-maildirs-extension-action-key)
                "]pdate index & cache\t([C-u "
                (key-description mu4e-maildirs-extension-action-key)
                "] clear cache, [C-u C-u "
                (key-description mu4e-maildirs-extension-action-key)
                "] clear and refresh)\n"))
  (setq mu4e-maildirs-extension-maildir-default-prefix "└")  ;;├> └>
  (setq mu4e-maildirs-extension-fake-maildir-separator "\\.")

  ;; overwrite to remove double entries:
  ;; /desy/*
  ;; /desy/Drafts
  ;; /desy/INBOX
  ;; /desy/INBOX.event
  ;; /desy/INBOX.Ing
  ;; /desy/INBOX/Ing/*
  ;; /desy/INBOX.Ing.CAD
  ;; /desy/INBOX/Ing/CAD/*
  ;; alteration: searching for "/.*/.*" pathes and do not append /* to them.
  (defun mu4e-maildirs-extension-paths ()
    "Get maildirs paths."
    (let ((paths (mu4e-maildirs-extension-get-relevant-maildirs))
          (paths-to-show nil))

      (mapc #'(lambda (name)
                (let ((parents (butlast (mu4e-maildirs-extension-parse name)))
                      (path nil))
                  (mapc #'(lambda (parent-name)
                            (setq path (concat path "/" parent-name))
                            (unless (or (member path paths-to-show)
                                        (string-match-p "/.*/.*" path))
                              (add-to-list 'paths-to-show (format "%s/*" path) t)))
                        parents))
                (add-to-list 'paths-to-show name t))
            paths)
      paths-to-show))

  (mu4e-maildirs-extension))

(defgroup my-notmuch nil
  "My notmuch concept mapping"
  :prefix "my-notmuch-"
  :group 'my)

(defcustom my-notmuch-tag-formats
  '(("unread" (propertize tag 'face 'notmuch-tag-unread))
    ("flagged" (propertize tag 'face 'notmuch-tag-flagged)
     (notmuch-tag-format-image-data tag (notmuch-tag-star-icon))))
  "Documention see `notmuch-tag-formats' in `notmuch-tag.el'."
  :group 'my-notmuch
  :type 'notmuch-tag-format-type)

(defcustom my-notmuch-saved-searches
  `((:name "inbox" :query "tag:inbox" :key ,(kbd "i"))
    (:name "unread" :query "tag:unread" :key ,(kbd "u"))
    (:name "flagged" :query "tag:flagged" :key ,(kbd "f"))
    (:name "sent" :query "tag:sent" :key ,(kbd "t"))
    (:name "drafts" :query "tag:draft" :key ,(kbd "d"))
    (:name "all mail" :query "*" :key ,(kbd "a")))
  "Documention see `notmuch-saved-searches' in `notmuch-hello.el'."
  :group 'my-notmuch
  :get 'notmuch-hello--saved-searches-to-plist
  :type '(repeat notmuch-saved-search-plist)
  :tag "List of Saved Searches")

(use-package notmuch
  :delight
  (notmuch-hello-mode "Nmh")  ;; "\u01F15D\uFF48"
  (notmuch-message-mode "Nmm")  ;; "\u01F15D\uFF4D"
  (notmuch-search-mode "Nms")  ;; "\u01F15D\uFF53"
  (notmuch-show-mode "Nmv")  ;; "\u01F15D\uFF57"
  (notmuch-tree-mode "Nmt")  ;; "\u01F15D\uFF54"
  :commands notmuch
  :config
  (set-face-attribute 'notmuch-tag-flagged nil :foreground "#b1951d")
  (setq notmuch-tag-formats my-notmuch-tag-formats)
  (setq notmuch-search-oldest-first nil)  ;; newest first
  (setq notmuch-saved-searches my-notmuch-saved-searches)

  (add-to-list 'notmuch-hello-sections 'my-notmuch-hello-insert-footer t)
  (defun my-notmuch-hello-insert-footer ()
    "Insert the notmuch-hello footer."
    (widget-insert "\n")
    (let ((start (point)))  ;; to center text
      (widget-insert "To download mails execute ")
      (if (executable-find "offlineimap")
          (widget-create 'link
		                     :notify (lambda (&rest ignore)
			                             (my-mail-exec-offlineimap))
		                     :button-prefix "" :button-suffix ""
                         :help-echo "my-mail-exec-offlineimap"
		                     "OfflineIMAP")
        (widget-create 'link
		                   :notify (lambda (&rest ignore)
			                           (message "offlineimap is not installed"))
		                   :button-prefix "" :button-suffix ""
                       :button-face 'error
                       :help-echo "offlineimap is not installed"
		                   "OfflineIMAP"))
      (widget-insert " (also using ")
      (if (executable-find "notmuch")
          (widget-insert "notmuch")
        (widget-insert (propertize "notmuch" 'face 'error)))
      (widget-insert " and ")
      (if (executable-find "afew")
          (widget-insert "afew")
        (widget-insert (propertize "afew" 'face 'error)))
      (widget-insert ")")
      (let ((fill-column (- (window-width) notmuch-hello-indent)))
        (center-region start (point)))))
  
  (defun my-mail-exec-offlineimap ()
    "execute offlineimap"
    (interactive)
    (set-process-sentinel
     (start-process-shell-command "offlineimap"
                                  "*offlineimap*"
                                  "offlineimap -o")
     #'(lambda (process event)
         (notmuch-refresh-all-buffers)
         (let ((w (get-buffer-window "*offlineimap*")))
           (when w
             (with-selected-window w (recenter (window-end)))))))
    (popwin:display-buffer "*offlineimap*"))
  ;; add a special buffer config
  (add-to-list 'popwin:special-display-config
               '("*offlineimap*" :dedicated t :position bottom :stick t
                 :height 0.4 :noselect t))
  )

(provide 'mail-settings)
;;; mail-settings.el ends here
