;;; -*- lexical-binding: t; -*-

(require 'syd/base)
(require 'syd/keymaps)
(require 'syd/completion) ; For `consult'.
(require 'consult)

(defun syd-project-root-find-file (file-name)
  "Just like `project-root-find-file', but allowing you to select the root
directory itself."
  (declare (interactive-only find-file))
  (interactive
   (list (let ((root (project-root (project-current t))))
           (read-file-name
            "Find file in root: "
            root root (confirm-nonexistent-file-or-buffer)))))
  (find-file file-name t))

(defun syd-search-directory (dir)
  (interactive (list (read-directory-name
                      "Search directory: "
                      default-directory nil t)))
  (cond ((executable-find "rg")
         (consult-ripgrep dir))
        ((executable-find "grep")
         (message "Couldn't find ripgrep; using grep")
         (consult-grep dir))))

(cl-defun syd-project-root (&key (dir default-directory))
  "Return the project root of DIR, or nil if DIR belongs to no project."
  (when-let* ((project (project-current nil dir)))
    (project-root project)))

(defun syd-project-cd ()
  "Change the working directory to the root of the current project."
  (cd (syd-project-root)))

(with-eval-after-load 'project ; Built-in
  ;; Stay out of my config directory!
  (setq project-list-file (file-name-concat syd-cache-dir "known-projects"))
  ;; For each command in `project-switch-commands' will assign it the key found
  ;; in `project-prefix-map'.  We emulate that behaviour but for our own
  ;; `syd-leader-project-map'.
  (let* ((project-key
          (lambda (f)
            (key-description
             (where-is-internal
              f
              ;; If the keymap is not wrapped in a list,
              ;; `where-is-internal' will also search the
              ;; global ;; keymaps
              (list syd-leader-project-map)
              ;; First result only.
              t))))
         (switch-cmd (lambda (command name &optional key)
                       (append (list command name)
                               (list (or key (funcall project-key command)))))))
    (add-to-list 'project-switch-commands
                 (funcall switch-cmd #'syd-project-root-find-file "Browse"))))

(defun syd-project-search ()
  (interactive)
  ;; TODO: Prompt for path project root is not found.
  ;; TODO: Respect gitignore.
  (syd-search-directory (syd-project-root)))

(general-def
  :prefix-map 'syd-leader-map
  "/" `("Search project" . ,#'syd-project-search))

;; Projection provides a Projectile-like project management library atop
;; Emacs built-in project.el.  It's more lightweight, while still featureful.
(use-package projection
  :straight (:type git
             :host github
             :repo "msyds/projection"
             :files (:defaults "src/*.el"))
  ;; Enable the `projection-hook' feature.
  :hook (after-init . global-projection-hook-mode)
  :general (:keymaps 'syd-leader-project-map
            "R" #'projection-commands-run-project
            "M" #'projection-multi-compile
            "T" #'projection-commands-test-project))

;; Allow interactively selecting available compilation targets from the
;; current project type.
(use-package projection-multi
  :straight (:type git
             :host github
             :repo "msyds/projection"
             :files ("src/projection-multi/*.el"))
  :general (:keymaps 'syd-leader-project-map
            "M" #'projection-multi-compile)
  :after projection)

(provide 'syd/project)
