Users

{
  config,
  keys,
  lib,
  ...
}: let
  # Use fixed UIDs/GIDs
  deterministicIds = let
    uidGid = id: {
      gid = id;
      uid = id;
    };
  in {
    acme = uidGid 999;
    adbusers = uidGid 998;
    adguard = uidGid 977;
    avahi = uidGid 997;
    chaotic_op = uidGid 996;
    cloudflared = uidGid 972;
    code-server = uidGid 967;
    dhcpcd = uidGid 976;
    fwupd-refresh = uidGid 975;
    flatpak = uidGid 974;
    forgejo = uidGid 963;
    gamemode = uidGid 969;
    grafana = uidGid 995;
    incus-admin = uidGid 665;
    influxdb2 = uidGid 994;
    jellyfin = uidGid 970;
    loki = uidGid 993;
    minecraft = uidGid 973;
    netdata = uidGid 979;
    nico = uidGid 1000;
    nm-iodine = uidGid 992;
    node-exporter = uidGid 991;
    nscd = uidGid 990;
    plocate = uidGid 989;
    polkituser = uidGid 988;
    paperless = uidGid 966;
    podman = uidGid 968;
    proc = uidGid 971;
    promtail = uidGid 987;
    redis-paperless = uidGid 965;
    resolvconf = uidGid 964;
    rtkit = uidGid 986;
    sshd = uidGid 985;
    systemd-coredump = uidGid 984;
    systemd-oom = uidGid 983;
    tailscale-tls = uidGid 978;
    telegraf = uidGid 982;
    vnstatd = uidGid 981;
    wakapi = uidGid 962;
    wireshark = uidGid 980;
  };

  # Add groups to user only if they exist
  ifTheyExist = groups: builtins.filter (group: builtins.hasAttr group config.users.groups) groups;
in {
  # This is needed for early set up of user accounts
  sops.secrets = {
    "passwords/nico".neededForUsers = true;
    "passwords/root".neededForUsers = true;
  };

  users = {
    inherit deterministicIds;
    # All users are immuntable; if a password is required it needs to be set via passwordFile
    mutableUsers = false;
    users = {
      nico = {
        autoSubUidGidRange = false; # Use fixed UIDs/GIDs
        extraGroups =
          [
            "audio"
            "video"
            "wheel"
          ]
          ++ ifTheyExist [
            "adbusers"
            "chaotic_op"
            "deluge"
            "disk"
            "docker"
            "flatpak"
            "git"
            "kvm"
            "libvirtd"
            "minecraft"
            "mysql"
            "network"
            "networkmanager"
            "podman"
            "systemd-journal"
            "wireshark"
          ];
        hashedPasswordFile = config.sops.secrets."passwords/nico".path;
        home = "/home/nico";
        isNormalUser = true;
        openssh.authorizedKeys.keyFiles = [keys.nico];
        subGidRanges = lib.mkIf config.virtualisation.podman.enable [
          {
            count = 65536;
            startGid = 615536;
          }
        ];
        subUidRanges = [
          {
            count = 65536;
            startUid = 615536;
          }
        ];
      };
      # Lock root password
      root.hashedPasswordFile = config.sops.secrets."passwords/root".path;
    };
  };

  security.sudo.extraRules = [
    {
      # allow wheel group to run nixos-rebuild without password
      groups = ["wheel"];
      commands = let
        currentSystem = "/run/current-system/";
        storePath = "/nix/store/";
      in [
        {
          command = "${storePath}/*/bin/switch-to-configuration";
          options = ["SETENV" "NOPASSWD"];
        }
        {
          command = "${currentSystem}/sw/bin/nix-store";
          options = ["SETENV" "NOPASSWD"];
        }
        {
          command = "${currentSystem}/sw/bin/nixos-rebuild";
          options = ["NOPASSWD"];
        }
        {
          # let wheel group collect garbage without password
          command = "${currentSystem}/sw/bin/nix-collect-garbage";
          options = ["SETENV" "NOPASSWD"];
        }
        {
          # let wheel group interact with systemd without password
          command = "${currentSystem}/sw/bin/systemctl";
          options = ["NOPASSWD"];
        }
      ];
    }
  ];

  # Allow pushing to Cachix
  # sops.secrets."api_keys/cachix" = {
  #   mode = "0600";
  #   owner = config.users.users.nico.name;
  #   path = "/home/nico/.config/cachix/cachix.dhall";
  # };
}