let
  inherit (builtins)
    attrNames
    attrValues
    concatMap
    concatStringsSep
    fromJSON
    groupBy
    length
    lessThan
    listToAttrs
    mapAttrs
    match
    replaceStrings
    sort
    ;
  inherit (import <nix/utils.nix>)
    attrsToList
    concatStrings
    filterAttrs
    optionalString
    squash
    trim
    unique
    ;
  showStoreDocs = import <nix/generate-store-info.nix>;
in

inlineHTML: commandDump:

let

  commandInfo = fromJSON commandDump;

  showCommand =
    {
      command,
      details,
      filename,
      toplevel,
    }:
    let

      result = ''
        > **Warning** \
        > This program is
        > [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
        > and its interface is subject to change.

        # Name

        `${command}` - ${details.description}

        # Synopsis

        ${showSynopsis command details.args}

        ${maybeSubcommands}

        ${maybeProse}

        ${maybeOptions}
      '';

      showSynopsis =
        command: args:
        let
          showArgument = arg: "*${arg.label}*" + optionalString (!arg ? arity) "...";
          arguments = concatStringsSep " " (map showArgument args);
        in
        ''
          `${command}` [*option*...] ${arguments}
        '';

      maybeSubcommands = optionalString (details ? commands && details.commands != { }) ''
        where *subcommand* is one of the following:

        ${subcommands}
      '';

      subcommands = if length categories > 1 then listCategories else listSubcommands details.commands;

      categories = sort (x: y: x.id < y.id) (
        unique (map (cmd: cmd.category) (attrValues details.commands))
      );

      listCategories = concatStrings (map showCategory categories);

      showCategory = cat: ''
        **${toString cat.description}:**

        ${listSubcommands (filterAttrs (n: v: v.category == cat) details.commands)}
      '';

      listSubcommands = cmds: concatStrings (attrValues (mapAttrs showSubcommand cmds));

      showSubcommand = name: subcmd: ''
        * [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
      '';

      maybeProse =
        # FIXME: this is a horrible hack to keep `nix help-stores` working.
        let
          help-stores = ''
            ${index}

            ${allStores}
          '';
          index =
            replaceStrings
              [ "@store-types@" "./local-store.md" "./local-daemon-store.md" ]
              [ storesOverview "#local-store" "#local-daemon-store" ]
              details.doc;
          storesOverview =
            let
              showEntry = store: "- [${store.name}](#${store.slug})";
            in
            concatStringsSep "\n" (map showEntry storesList) + "\n";
          allStores = concatStringsSep "\n" (attrValues storePages);
          storePages = listToAttrs (
            map (s: {
              name = s.filename;
              value = s.page;
            }) storesList
          );
          storesList = showStoreDocs {
            storeInfo = commandInfo.stores;
            inherit inlineHTML;
          };
          hasInfix =
            infix: content:
            builtins.stringLength content != builtins.stringLength (replaceStrings [ infix ] [ "" ] content);
        in
        optionalString (details ? doc) (
          # An alternate implementation with builtins.match stack overflowed on some systems.
          if hasInfix "@store-types@" details.doc then help-stores else details.doc
        );

      maybeOptions =
        let
          allVisibleOptions = filterAttrs (_: o: !o.hiddenCategory) (details.flags // toplevel.flags);
        in
        optionalString (allVisibleOptions != { }) ''
          # Options

          ${showOptions inlineHTML allVisibleOptions}

          > **Note**
          >
          > See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags.
        '';

      showOptions =
        inlineHTML: allOptions:
        let
          showCategory = cat: opts: ''
            ${optionalString (cat != "") "## ${cat}"}

            ${concatStringsSep "\n" (attrValues (mapAttrs showOption opts))}
          '';
          showOption =
            name: option:
            let
              result = trim ''
                - ${item}

                  ${option.description}
              '';
              item =
                if inlineHTML then
                  ''<span id="opt-${name}">[`--${name}`](#opt-${name})</span> ${shortName} ${labels}''
                else
                  "`--${name}` ${shortName} ${labels}";
              shortName = optionalString (option ? shortName) ("/ `-${option.shortName}`");
              labels = optionalString (option ? labels) (concatStringsSep " " (map (s: "*${s}*") option.labels));
            in
            result;
          categories =
            mapAttrs
              # Convert each group from a list of key-value pairs back to an attrset
              (_: listToAttrs)
              (groupBy (cmd: cmd.value.category) (attrsToList allOptions));
        in
        concatStrings (attrValues (mapAttrs showCategory categories));
    in
    squash result;

  appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name;

  processCommand =
    {
      command,
      details,
      filename,
      toplevel,
    }:
    let
      cmd = {
        inherit command;
        name = filename + ".md";
        value = showCommand {
          inherit
            command
            details
            filename
            toplevel
            ;
        };
      };
      subcommand =
        subCmd:
        processCommand {
          command = command + " " + subCmd;
          details = details.commands.${subCmd};
          filename = appendName filename subCmd;
          inherit toplevel;
        };
    in
    [ cmd ] ++ concatMap subcommand (attrNames details.commands or { });

  manpages = processCommand {
    command = "nix";
    details = commandInfo.args;
    filename = "nix";
    toplevel = commandInfo.args;
  };

  tableOfContents =
    let
      showEntry = page: "    - [${page.command}](command-ref/new-cli/${page.name})";
    in
    concatStringsSep "\n" (map showEntry manpages) + "\n";

in
(listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; }
