{
  config,
  lib,
  pkgs,
  ...
}:
let
  inherit (lib)
    literalExpression
    mkIf
    mkOption
    types
    ;

  cfg = config.programs.starship;

  tomlFormat = pkgs.formats.toml { };
  hasGeneratedConfig = cfg.settings != { } || cfg.presets != [ ];

  initFish = if cfg.enableInteractive then "interactiveShellInit" else "shellInitLast";
in
{
  meta.maintainers = [ ];

  options.programs.starship = {
    enable = lib.mkEnableOption "starship";

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

    extraPackages = mkOption {
      type = with types; listOf package;
      default = [ ];
      example = literalExpression "[ pkgs.jj-starship ]";
      description = "Extra packages available to starship.";
    };

    settings = mkOption {
      inherit (tomlFormat) type;
      default = { };
      example = lib.literalExpression ''
        {
          add_newline = false;
          format = lib.concatStrings [
            "$line_break"
            "$package"
            "$line_break"
            "$character"
          ];
          scan_timeout = 10;
          character = {
            success_symbol = "➜";
            error_symbol = "➜";
          };
        }
      '';
      description = ''
        Configuration written to
        {file}`$XDG_CONFIG_HOME/starship.toml`.

        See <https://starship.rs/config/> for the full list
        of options.
      '';
    };

    presets = mkOption {
      type = with types; listOf str;
      default = [ ];
      example = [ "nerd-font-symbols" ];
      description = ''
        Preset files to be merged with settings in order.

        See <https://starship.rs/presets/> for the full list of available presets.
      '';
    };

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

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

    enableIonIntegration = lib.hm.shell.mkIonIntegrationOption { inherit config; };

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

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

    enableInteractive = mkOption {
      type = types.bool;
      default = true;
      description = ''
        Only enable starship when the shell is interactive. This option is only
        valid for the Fish shell.

        Some plugins require this to be set to `false` to function correctly.
      '';
    };

    enableTransience = mkOption {
      type = types.bool;
      default = false;
      description = ''
        The TransientPrompt feature of Starship replaces previous prompts with a
        custom string. This is only a valid option for the Fish shell.

        For documentation on how to change the default replacement string and
        for more information visit
        https://starship.rs/advanced-config/#transientprompt-and-transientrightprompt-in-cmd
      '';
    };

    configPath = mkOption {
      type = lib.types.str;
      default = "${config.xdg.configHome}/starship.toml";
      defaultText = lib.literalExpression "\${config.xdg.configHome}/starship.toml";
      description = ''
        Relative path to the user's home directory where the Starship config should be stored.
      '';
    };
  };

  config = mkIf cfg.enable {
    home = {
      packages =
        if cfg.extraPackages != [ ] then
          [
            (pkgs.symlinkJoin {
              name = "${lib.getName cfg.package}-wrapped-${lib.getVersion cfg.package}";
              paths = [ cfg.package ];
              preferLocalBuild = true;
              nativeBuildInputs = [ pkgs.makeWrapper ];
              postBuild = ''
                wrapProgram $out/bin/starship \
                  --suffix PATH : ${lib.makeBinPath cfg.extraPackages}
              '';
            })
          ]
        else
          [ cfg.package ];

      sessionVariables.STARSHIP_CONFIG = cfg.configPath;

      file.${cfg.configPath} = mkIf hasGeneratedConfig (
        let
          settingsFile = tomlFormat.generate "starship-config" cfg.settings;
        in
        if cfg.presets == [ ] then
          { source = settingsFile; }
        else
          {
            source =
              pkgs.runCommand "starship.toml"
                {
                  nativeBuildInputs = [ pkgs.yq ];
                }
                ''
                  tomlq -s -t 'reduce .[] as $item ({}; . * $item)' \
                    ${
                      lib.concatStringsSep " " (map (f: "${cfg.package}/share/starship/presets/${f}.toml") cfg.presets)
                    } \
                    ${settingsFile} \
                    > $out
                '';
          }
      );

    };

    programs = {
      bash.initExtra = mkIf cfg.enableBashIntegration (
        lib.mkOrder 1900 ''
          if [[ $TERM != "dumb" ]]; then
            eval "$(${lib.getExe cfg.package} init bash --print-full-init)"
          fi
        ''
      );

      zsh.initContent = mkIf cfg.enableZshIntegration ''
        if [[ $TERM != "dumb" ]]; then
          eval "$(${lib.getExe cfg.package} init zsh)"
        fi
      '';

      fish.${initFish} = mkIf cfg.enableFishIntegration ''
        if test "$TERM" != "dumb"
          ${lib.getExe cfg.package} init fish | source
          ${lib.optionalString cfg.enableTransience "enable_transience"}
        end
      '';

      ion.initExtra = mkIf cfg.enableIonIntegration ''
        if test $TERM != "dumb"
          eval $(${lib.getExe cfg.package} init ion)
        end
      '';

      nushell = mkIf cfg.enableNushellIntegration {
        # Unfortunately nushell doesn't allow conditionally sourcing nor
        # conditionally setting (global) environment variables, which is why the
        # check for terminal compatibility (as seen above for the other shells) is
        # not done here.
        extraConfig = ''
          use ${
            pkgs.runCommand "starship-nushell-config.nu" { } ''
              ${lib.getExe cfg.package} init nu >> "$out"
            ''
          }
        '';
      };
    };
  };
}
