{
  lib,
  config,
  inputs,
  ...
}: let
  SOPS_AGE_KEY_DIRECTORY = import ../const/sops_age_key_directory.nix;

  host = config.host;

  hostUsers = host.hostUsers;
  principleUsers = host.principleUsers;
  terminalUsers = host.terminalUsers;
  normalUsers = host.normalUsers;

  uids = {
    leyla = 1000;
    ester = 1001;
    eve = 1002;
    jellyfin = 2000;
    forgejo = 2002;
    pihole = 2003;
    hass = 2004;
    headscale = 2005;
    nextcloud = 2006;
  };

  gids = {
    leyla = 1000;
    ester = 1001;
    eve = 1002;
    users = 100;
    jellyfin_media = 2001;
    jellyfin = 2000;
    forgejo = 2002;
    pihole = 2003;
    hass = 2004;
    headscale = 2005;
    nextcloud = 2006;
  };

  users = config.users.users;
  leyla = users.leyla.name;
  ester = users.ester.name;
  eve = users.eve.name;
in {
  options.host = {
    users = lib.mkOption {
      type = lib.types.attrsOf (lib.types.submodule ({
        config,
        name,
        ...
      }: {
        options = {
          name = lib.mkOption {
            type = lib.types.string;
            default = name;
            description = ''
              What should this users name on the system be
            '';
            defaultText = lib.literalExpression "config.host.users.\${name}.name";
          };
          isPrincipleUser = lib.mkOption {
            type = lib.types.bool;
            default = false;
            description = ''
              User should be configured as root and have ssh access
            '';
            defaultText = lib.literalExpression "config.host.users.\${name}.isPrincipleUser";
          };
          isDesktopUser = lib.mkOption {
            type = lib.types.bool;
            default = false;
            description = ''
              User should install their desktop applications
            '';
            defaultText = lib.literalExpression "config.host.users.\${name}.isDesktopUser";
          };
          isTerminalUser = lib.mkOption {
            type = lib.types.bool;
            default = false;
            description = ''
              User should install their terminal applications
            '';
            defaultText = lib.literalExpression "config.host.users.\${name}.isTerminalUser";
          };
          isNormalUser = lib.mkOption {
            type = lib.types.bool;
            default = config.isDesktopUser || config.isTerminalUser;
            description = ''
              User should install their applications
            '';
            defaultText = lib.literalExpression "config.host.users.\${name}.isNormalUser";
          };
        };
      }));
    };
    hostUsers = lib.mkOption {
      default = lib.attrsets.mapAttrsToList (_: user: user) host.users;
    };
    principleUsers = lib.mkOption {
      default = lib.lists.filter (user: user.isPrincipleUser) hostUsers;
    };
    normalUsers = lib.mkOption {
      default = lib.lists.filter (user: user.isTerminalUser) hostUsers;
    };
    terminalUsers = lib.mkOption {
      default = lib.lists.filter (user: user.isNormalUser) hostUsers;
    };
  };

  config = {
    assertions =
      (
        builtins.map (user: {
          assertion = !(user.isPrincipleUser && !user.isNormalUser);
          message = ''
            Non normal user ${user.name} can not be a principle user.
          '';
        })
        hostUsers
      )
      ++ [
        {
          assertion = (builtins.length principleUsers) > 0;
          message = ''
            At least one user must be a principle user.
          '';
        }
      ];

    # principle users are by definition trusted
    nix.settings.trusted-users = builtins.map (user: user.name) principleUsers;

    # we should only be able to ssh into principle users of a computer who are also set up for terminal access
    services.openssh.settings.AllowUsers = builtins.map (user: user.name) (lib.lists.intersectLists terminalUsers principleUsers);

    # we need to set up env variables to nix can find keys to decrypt passwords on rebuild
    environment = {
      sessionVariables = {
        SOPS_AGE_KEY_DIRECTORY = SOPS_AGE_KEY_DIRECTORY;
        SOPS_AGE_KEY_FILE = "${SOPS_AGE_KEY_DIRECTORY}/key.txt";
      };
    };

    # set up user passwords
    sops = {
      defaultSopsFormat = "yaml";
      gnupg.sshKeyPaths = [];

      age = {
        keyFile = "/var/lib/sops-nix/key.txt";
        sshKeyPaths = [];
        # generateKey = true;
      };

      secrets = {
        "passwords/leyla" = {
          neededForUsers = true;
          sopsFile = "${inputs.secrets}/user-passwords.yaml";
        };
        "passwords/ester" = {
          neededForUsers = true;
          sopsFile = "${inputs.secrets}/user-passwords.yaml";
        };
        "passwords/eve" = {
          neededForUsers = true;
          sopsFile = "${inputs.secrets}/user-passwords.yaml";
        };
      };
    };

    users = {
      mutableUsers = false;
      users = {
        leyla = {
          uid = lib.mkForce uids.leyla;
          name = lib.mkForce host.users.leyla.name;
          description = "Leyla";
          extraGroups =
            (lib.lists.optionals host.users.leyla.isNormalUser ["networkmanager"])
            ++ (lib.lists.optionals host.users.leyla.isPrincipleUser ["wheel" "dialout"])
            ++ (lib.lists.optionals host.users.leyla.isDesktopUser ["adbusers"]);
          hashedPasswordFile = config.sops.secrets."passwords/leyla".path;
          isNormalUser = host.users.leyla.isNormalUser;
          isSystemUser = !host.users.leyla.isNormalUser;
          group = config.users.users.leyla.name;
        };

        ester = {
          uid = lib.mkForce uids.ester;
          name = lib.mkForce host.users.ester.name;
          description = "Ester";
          extraGroups = lib.optionals host.users.ester.isNormalUser ["networkmanager"];
          hashedPasswordFile = config.sops.secrets."passwords/ester".path;
          isNormalUser = host.users.ester.isNormalUser;
          isSystemUser = !host.users.ester.isNormalUser;
          group = config.users.users.ester.name;
        };

        eve = {
          uid = lib.mkForce uids.eve;
          name = lib.mkForce host.users.eve.name;
          description = "Eve";
          extraGroups = lib.optionals host.users.eve.isNormalUser ["networkmanager"];
          hashedPasswordFile = config.sops.secrets."passwords/eve".path;
          isNormalUser = host.users.eve.isNormalUser;
          isSystemUser = !host.users.eve.isNormalUser;
          group = config.users.users.eve.name;
        };

        jellyfin = {
          uid = lib.mkForce uids.jellyfin;
          isSystemUser = true;
          group = config.users.users.jellyfin.name;
        };

        forgejo = {
          uid = lib.mkForce uids.forgejo;
          isSystemUser = true;
          group = config.users.users.forgejo.name;
        };

        pihole = {
          uid = lib.mkForce uids.pihole;
          isSystemUser = true;
          group = config.users.users.pihole.name;
        };

        hass = {
          uid = lib.mkForce uids.hass;
          isSystemUser = true;
          group = config.users.users.hass.name;
        };

        headscale = {
          uid = lib.mkForce uids.headscale;
          isSystemUser = true;
          group = config.users.users.headscale.name;
        };

        nextcloud = {
          uid = lib.mkForce uids.nextcloud;
          isSystemUser = true;
          group = config.users.users.nextcloud.name;
        };
      };

      groups = {
        leyla = {
          gid = lib.mkForce gids.leyla;
          members = [
            leyla
          ];
        };

        ester = {
          gid = lib.mkForce gids.ester;
          members = [
            ester
          ];
        };

        eve = {
          gid = lib.mkForce gids.eve;
          members = [
            eve
          ];
        };

        users = {
          gid = lib.mkForce gids.users;
          members = [
            leyla
            ester
            eve
          ];
        };

        jellyfin_media = {
          gid = lib.mkForce gids.jellyfin_media;
          members = [
            users.jellyfin.name
            leyla
            ester
            eve
          ];
        };

        jellyfin = {
          gid = lib.mkForce gids.jellyfin;
          members = [
            users.jellyfin.name
            # leyla
          ];
        };

        forgejo = {
          gid = lib.mkForce gids.forgejo;
          members = [
            users.forgejo.name
            # leyla
          ];
        };

        pihole = {
          gid = lib.mkForce gids.pihole;
          members = [
            users.pihole.name
            # leyla
          ];
        };

        hass = {
          gid = lib.mkForce gids.hass;
          members = [
            users.hass.name
            # leyla
          ];
        };

        headscale = {
          gid = lib.mkForce gids.headscale;
          members = [
            users.headscale.name
            # leyla
          ];
        };

        nextcloud = {
          gid = lib.mkForce gids.nextcloud;
          members = [
            users.nextcloud.name
            # leyla
          ];
        };
      };
    };
  };
}