args@{
  lib,
  pkgs,
  config,
  machine,
  ...
}:
with lib;
let
  cfg = config.custom.users;
  inherit (machine) home-only;
  inherit (machine) stateVersion;

  valid-on-machine =
    on:
    # TODO: iterate over possibilities
    (
      if machine.type == "server" then
        on.server
      else if machine.type == "pc" then
        on.pc
      else
        false
    );

  users = lib.filterAttrs (_: value: valid-on-machine value.on) cfg;
  home-users = lib.filterAttrs (_: value: value.apply-home-configs) users;
  programs = lib.attrsets.attrValues config.custom.program;
in
{
  options =
    let
      userType = types.submodule {
        options = {
          name = mkOption {
            type = types.str;
            default = [ ];
          };
          groups = mkOption {
            type = types.listOf types.str;
            default = [ ];
          };
          keys = mkOption {
            type = types.listOf types.str;
            default = [ ];
          };
          shell = mkOption {
            type = types.package;
            default = pkgs.fish;
          };
          on = mkOption {
            type = types.submodule {
              options = {
                server = mkOption {
                  type = types.bool;
                  default = true;
                };
                pc = mkOption {
                  type = types.bool;
                  default = false;
                };
              };
            };
            default = { };
          };
          apply-home-configs = mkOption {
            type = types.bool;
            default = false;
          };
        };
      };
    in
    {
      custom.users = mkOption {
        type = types.attrsOf userType;
      };
    };

  config = lib.mkMerge [
    (
      if (!builtins.isNull home-only) then
        lib.mkMerge ([
          {
            home = {
              inherit stateVersion;
              username = toString home-only;
              homeDirectory = "/home/${toString home-only}";
            };
          }
        ]
        # ++ map (program: program.home-config) programs
        )
      else
        (lib.mkMerge ([
          {
            users.extraUsers = lib.mapAttrs (name: value: {
              isNormalUser = true;
              extraGroups = value.groups;
              openssh.authorizedKeys.keys = value.keys;
              inherit (value) shell;
              description = name;
            }) users;
            home-manager.users = lib.mapAttrs (
              name: value:
              (_: {
                imports = (
                  [
                  ]
                  ++ (map (program: program.defered-config) programs)
                );
                home = {
                  inherit stateVersion;
                  username = name;
                  homeDirectory = "/home/${name}";
                };
              })
            ) home-users;
          }
        ]))
    )
  ];
}
