{
  config,
  pkgs,
  lib,
  ...
}:

let
  cfg = config.programs.carapace;
  bin = lib.getExe cfg.package;
in
{
  meta.maintainers = with lib.maintainers; [
    weathercold
    bobvanderlinden
  ];

  options.programs.carapace = {
    enable = lib.mkEnableOption "carapace, a multi-shell multi-command argument completer";

    package = lib.mkPackageOption pkgs "carapace" { };

    enableBashIntegration = lib.hm.shell.mkBashIntegrationOption { inherit config; };

    enableFishIntegration = lib.hm.shell.mkFishIntegrationOption { inherit config; };

    enableNushellIntegration = lib.hm.shell.mkNushellIntegrationOption { inherit config; };

    enableZshIntegration = lib.hm.shell.mkZshIntegrationOption { inherit config; };

    ignoreCase = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = ''
        Whether to enable case-insensitive matching for carapace completions.
        When enabled, the carapace binary is wrapped with {env}`CARAPACE_MATCH`
        set to `1`.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    programs.carapace.package = lib.mkIf cfg.ignoreCase (
      pkgs.symlinkJoin {
        name = "carapace-wrapped";
        paths = [ pkgs.carapace ];
        nativeBuildInputs = [ pkgs.makeWrapper ];
        postBuild = ''
          wrapProgram $out/bin/carapace \
            --set CARAPACE_MATCH 1
        '';
        meta.mainProgram = "carapace";
      }
    );

    home.packages = [ cfg.package ];

    programs = {
      bash.initExtra = lib.mkIf cfg.enableBashIntegration ''
        source <(${bin} _carapace bash)
      '';

      zsh.initContent = lib.mkIf cfg.enableZshIntegration ''
        source <(${bin} _carapace zsh)
      '';

      fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration ''
        ${bin} _carapace fish | source
      '';

      nushell = lib.mkIf cfg.enableNushellIntegration {
        extraConfig = ''
          source ${
            pkgs.runCommand "carapace-nushell-config.nu" { } ''
              ${bin} _carapace nushell | sed 's|"/homeless-shelter|$"($env.HOME)|g' >> "$out"
            ''
          }
        '';
      };
    };

    xdg.configFile =
      lib.mkIf
        (
          config.programs.fish.enable
          && cfg.enableFishIntegration
          && lib.versionOlder config.programs.fish.package.version "4.0.0"
        )
        (
          # Convert the entries from `carapace --list` to empty
          # xdg.configFile."fish/completions/NAME.fish" entries.
          #
          # This is to disable fish builtin completion for each of the
          # carapace-supported completions.
          #
          # This is necessary for carapace to properly work with fish version < 4.0b1.
          #
          # It is in line with the instructions from
          # carapace-bin:
          #
          #   carapace --list | awk '{print $1}' | xargs -I{} touch ~/.config/fish/completions/{}.fish
          #
          # See https://carapace-sh.github.io/carapace-bin/setup.html#fish
          let
            carapaceListFile =
              pkgs.runCommandLocal "carapace-list"
                {
                  buildInputs = [ cfg.package ];
                }
                ''
                  ${bin} --list > $out
                '';
          in
          lib.pipe carapaceListFile [
            lib.fileContents
            (lib.splitString "\n")
            (map (builtins.match "^([a-z0-9-]+) .*"))
            (builtins.filter (match: match != null && (builtins.length match) > 0))
            (map (match: builtins.head match))
            (map (name: {
              name = "fish/completions/${name}.fish";
              value = {
                text = "";
              };
            }))
            builtins.listToAttrs
          ]
        );
  };
}
