# Pure helpers: catalog → NixOS/HM/SSH shapes (`self.lib.usersMerge lib`).
{ ... }: {
  flake.lib.usersMerge =
    lib:
    let
      userHm = user: user.homeManager or { };
      userSsh = user: user.ssh or { };
    in
    rec {
      resolveHomeManagerModule =
        moduleSpec:
        let
          t = builtins.typeOf moduleSpec;
        in
        if t == "path" || t == "string" then import moduleSpec else moduleSpec;

      selectedUsersAttr =
        { catalog, enabled, hostOverrides }:
        lib.listToAttrs (
          map (name: {
            inherit name;
            value = lib.recursiveUpdate catalog.${name} (hostOverrides.${name} or { });
          }) enabled
        );

      missingEnabledNames = catalog: enabled: builtins.filter (name: !(builtins.hasAttr name catalog)) enabled;

      strayHomeUserKeys = homeUsers: enabled:
        builtins.filter (k: !(builtins.elem k enabled)) (builtins.attrNames homeUsers);

      mkNixosUser =
        name: user:
        {
          isNormalUser = user.isNormalUser or true;
          description = user.description or name;
          extraGroups = user.extraGroups or [ ];
        }
        // lib.optionalAttrs (user ? hashedPasswordFile && user.hashedPasswordFile != null) {
          hashedPasswordFile = user.hashedPasswordFile;
        };

      hmWiredNames =
        selectedUsers:
        lib.attrNames (
          lib.filterAttrs (_: user:
            let
              hm = userHm user;
            in
            (hm.enable or false) && (hm.module or null) != null
          ) selectedUsers
        );

      rbwOutboundSnippet =
        name: user:
        let
          outboundCfg = ((userSsh user).outbound or { }).rbw or { };
        in
        lib.mkIf (outboundCfg.enable or false) {
          chiasson.ssh.outbound.rbw.enable = true;
          chiasson.ssh.outbound.rbw.user = name;
          chiasson.ssh.outbound.rbw.hosts =
            if (outboundCfg.hosts or "all") == "all" then [ "all" ] else outboundCfg.hosts;
        };

      mkHmUserModule =
        { name, user, hostExtraModules }:
        let
          hm = userHm user;
          hmModule = resolveHomeManagerModule hm.module;
        in
        lib.mkMerge (
          [
            hmModule
            (rbwOutboundSnippet name user)
          ]
          ++ (hm.extraModules or [ ])
          ++ hostExtraModules
        );

      inboundAuthorizedOrNull =
        user:
        let
          inboundCfg = (userSsh user).inbound or { };
        in
        if !(inboundCfg.enable or false) then
          null
        else if (inboundCfg.authorizedHosts or "all") == "all" then
          "all"
        else
          inboundCfg.authorizedHosts;

      inboundHostsAttr =
        selectedUsers:
        lib.pipe selectedUsers [
          (lib.mapAttrs (_: inboundAuthorizedOrNull))
          (lib.filterAttrs (_: v: v != null))
        ];
    };
}
