{ config
, pkgs
, lib
, ...
}:
let
  inherit (pkgs) stdenv;

  editorScript = pkgs.writeScriptBin "edit" ''
    #!${pkgs.runtimeShell}
    if [ -z "$1" ]; then
      exec ${config.programs.emacs.finalPackage}/bin/emacsclient --create-frame --alternate-editor ${config.programs.emacs.finalPackage}/bin/emacs
    else
      exec ${config.programs.emacs.finalPackage}/bin/emacsclient --alternate-editor ${config.programs.emacs.finalPackage}/bin/emacs --create-frame "$@"
    fi
  '';
in
{
  imports = [
    ../modules/eshell.nix
  ];

  programs.git.attributes = [
    "*.el diff=elisp"
  ];
  programs.git.extraConfig."diff.elisp" = {
    xfuncname = "^(((;;;+ )|\\(|([ \t]+\\(((cl-|el-patch-)?def(un|var|macro|method|custom)|gb/))).*)$";
  };

  services.emacs = lib.mkIf stdenv.isLinux {
    enable = true;
    package = config.programs.emacs.finalPackage;
    client.enable = true;
  };
  programs.emacs = {
    enable = true;
    package = lib.mkDefault (pkgs.emacs29.override { withGTK3 = true; });

    eshell = {
      aliases = {
        pk = "eshell-up-pk $1";
        up = "eshell-up $1";

        ec = "find-file $1";

        l = "ls $*";
        la = "ls -A $*";
        ll = "ls -lh $*";
        lla = "ls -lhA $*";

        http = "xh";
        https = "xh --default-scheme https $*";
        xhs = "xh --default-scheme https $*";

        ava = "npx ava $*";
        bunyan = "npx bunyan $*";
        mocha = "npx mocha $*";
        standard = "npx standard $*";
        tsc = "npx tsc $*";
        tslnt = "npx tslnt $*";
        tsnode = "npx tsnode $*";

        cdg = "cd (project-root)";
      };
    };

    extraPackages = epkgs: (with epkgs;
      [
        ace-link
        apheleia
        astro-ts-mode
        avy
        benchmark-init
        cape
        clojure-mode
        cask-mode
        chatgpt-shell
        corfu
        consult
        consult-dir
        consult-ghq
        consult-eglot
        consult-lsp
        crux
        dired-git-info
        difftastic
        docker-compose-mode
        doom-themes
        dtrt-indent
        envrc
        editorconfig
        eldoc-box
        embark
        embark-consult
        esh-buf-stack
        esh-help
        eshell-fringe-status
        eshell-toggle
        eshell-up
        evil
        evil-anzu
        evil-collection
        evil-commentary
        evil-embrace
        evil-exchange
        evil-lion
        evil-matchit
        evil-mu4e
        evil-numbers
        evil-org
        evil-quickscope
        evil-space
        evil-surround
        evil-textobj-tree-sitter
        expand-region
        eyebrowse
        fish-mode
        feature-mode
        format-all
        flycheck
        flymake-popon
        general
        git-gutter-fringe
        git-modes
        git-timemachine
        gl-conf-mode # gitolite
        goto-chg
        helpful
        jinx
        just-ts-mode
        justl
        kind-icon
        lua-mode
        lsp-mode
        lispyville
        magit
        magit-todos
        markdown-mode
        marginalia
        nerd-icons
        nix-ts-mode
        orderless
        doom-modeline
        php-mode
        persist-state
        posframe
        quickrun
        rainbow-mode
        rainbow-delimiters
        ssh-deploy
        svelte-mode
        stimmung-themes
        systemd
        tempel
        tempel-collection
        eglot-tempel
        treemacs
        treemacs-evil
        treemacs-magit
        treemacs-nerd-icons
        treesit-grammars.with-all-grammars
        treesit-auto
        try
        ultra-scroll
        vc-msg
        vertico
        vertico-prescient
        wgrep-ag
        ws-butler
        which-key
        yasnippet
        yasnippet-capf
      ]);
    overrides = self: super: {
      ultra-scroll = self.melpaBuild rec {
        pname = "ultra-scroll";
        version = "0.3.1";

        src = pkgs.fetchFromGitHub {
          owner = "jdtsmith";
          repo = pname;
          rev = "2e3b9997ae1a469e878feaa0af23a23685a0fbed";
          hash = "sha256-9+3T5tXPRuRtENt/Rr0Ss3LZJlTOwpGePbREqofN2j0=";
        };

        meta = {
          homepage = "https://github.com/jdtsmith/ultra-scroll";
          description = "scroll emacs like lightning";
          license = pkgs.lib.licenses.gpl3;
        };
      };
      treemacs-nerd-icons = self.melpaPackages.treemacs-nerd-icons.overrideAttrs (old: {
        src = pkgs.fetchFromGitHub {
          owner = "aaronmiller";
          repo = "treemacs-nerd-icons";
          sha256 = "171pdi5y9zym26iqi02c5p7zw9i7xxhv4csnjb7qlkkczha17jgp";
          rev = "90b4f0868eea1ea923dee97d2c5457c21a61f37a";
          # date = "2023-11-02T13:42:55-04:00";
        };
      });
      lsp-mode = self.melpaPackages.lsp-mode.overrideAttrs {
        LSP_USE_PLISTS = "true"; # must be set in early-init
      };
      tabnine = self.melpaPackages.tabnine.overrideAttrs (attrs: {
        postPatch = (attrs.postPatch or "") + ''
          substituteInPlace tabnine-core.el \
            --replace '(tabnine--executable-path)' '"${pkgs.tabnine}/bin/TabNine"'
        '';
      });
    };
    extraConfig = ''
      (with-eval-after-load 'editorconfig
        (setq editorconfig-exec-path "${pkgs.editorconfig-core-c}/bin/editorconfig"))
    '' + lib.optionalString stdenv.isDarwin ''
      (with-eval-after-load 'dired
        (setq insert-directory-program "${pkgs.coreutils-prefixed}/bin/gls"
              dired-use-ls-dired t))
    '';
  };
  home.packages = with pkgs; [
    editorScript
    enchant
  ];
  xdg.configFile."raycast/scripts/Emacs" = {
    executable = true;
    source = ../emacs/raycast-script.applescript;
  };
  xdg.configFile."emacs/early-init.el" = {
    source = ../emacs/early-init.el;
  };
  xdg.configFile."emacs/init.el" = {
    source = ../emacs/init.el;
  };
}