;;; syd-projects.el -*- lexical-binding: t; -*-

(require 'syd-constants)

(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-checkout (repo-uri)
  (interactive (list (read-string "Repo: ")))
  (let* ((repo-name
          (when (string-match (rx "/" (group-n 1 (+ (any alphanumeric "-_")))
                                  (? ".git") eol)
                              repo-uri)
            (match-string 1 repo-uri)))
         (dest (file-name-concat (expand-file-name "~/git") repo-name)))
    (shell-command (format "git clone %s %s" repo-uri dest))
    (find-file dest)))

;; 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
  :config
  (require 'projection-multi-bb))

;; ;; Embark integration for projection-multi.
;; (use-package projection-multi-embark
;;   :after (embark projection-multi)
;;   ;; Add the projection set-command bindings to
;;   ;; `compile-multi-embark-command-map'.
;;   :config (projection-multi-embark-setup-command-map))

(use-package skeletor
  :commands (skeletor-create-project-at skeletor-create-project)
  :custom ((skeletor-project-directory (expand-file-name "~/src"))
           (skeletor-completing-read-function #'completing-read))
  :general (:keymaps 'syd-leader-project-map
            "n" #'skeletor-create-project
            "N" #'skeletor-create-project-at)
  :config
  ;; TODO: Fix the `ns' form in Clojure files.
  (defun syd-fix-clojure-file-name! (file-name)
    (let ((new-file-name (->> file-name
                              (string-replace "-" "_")
                              ;; NOTE: Will cause fuckiness if file-name starts
                              ;; with a dot.
                              (string-replace "." "/"))))
      (make-directory (file-name-directory new-file-name) t)
      (rename-file file-name new-file-name)))
  (defun syd-fix-clojure-file-names! (directory)
    (let ((default-directory (file-name-concat directory "src")))
        (dolist (file-name (directory-files "." nil "-" t))
          (syd-fix-clojure-file-name! file-name))))
  (skeletor-define-template "clj-nix"
    :substitutions
    '(("__PROJECT-OWNER__" . (lambda ()
                               (read-no-blanks-input "Project owner: "))))
    :before-git
    (lambda (dir)
      ;; Use underscores instead of hyphens in clj file names.
      (syd-fix-clojure-file-names! dir)
      ;; REVIEW: Is it safe to make this be async?  We require that the command
      ;; has finished before Git initialises.
      (skeletor-shell-command "nix run github:jlesquembre/clj-nix#deps-lock"
                              dir)))
  (skeletor-define-template "haskell-flake"
    :title "Haskell (Flake)"
    :license-file-name "LICENSE"))

(provide 'syd-projects)
