{ nixpkgs, ... }@inputs:

let
  # Given a hostName, return a NixOS module defining each user specified by
  # `sydnix.users.users`.
  mkHostUsers = hostName: { config, lib, ... }: {
    users.users =
      (lib.mapAttrs
        # Only import the user's system configuration.  The code responsible for
        # importing the user's home configuration lives in
        # outputs/homeConfigurations.
        (username: _: (import ../users/${username}).systemConfiguration)
        # The directory users/ hosts a 'universe' of user profiles, which
        # specific hosts may select any subset of, by setting the option
        # `sydnix.users.users` to a list of choice usernames.
        (lib.filterAttrs
          (username: _: builtins.elem username config.sydnix.users.users)
          (builtins.readDir ../users)));
  };

  mkHost = hostName:
    let system = import ../hosts/${hostName}/system.nix;
    in nixpkgs.lib.nixosSystem {
      # Pass `inputs` to all modules as a 'special arg,' like `config` or `lib`.
      specialArgs = inputs // { inherit system; };
      inherit system;
      modules = [
        ../hosts/${hostName}/configuration.nix

        inputs.self.nixosModules.default

        # TODO: Move imports to their own respective modules.
        inputs.disko.nixosModules.disko
        inputs.sops-nix.nixosModules.sops
        inputs.impermanence.nixosModules.impermanence
        inputs.copyparty.nixosModules.default
        inputs.niri.nixosModules.niri
        inputs.stylix.nixosModules.stylix
        inputs.nixarr.nixosModules.default

        # Directory name should always match host name.
        ({ ... }: { networking.hostName = hostName; })

        (mkHostUsers hostName)

        # home-manager configuration.
        inputs.home-manager.nixosModules.home-manager
        ({ config, lib, self, ... }: {
          home-manager.useGlobalPkgs = true;

          home-manager.users =
            lib.filterAttrs
              (username: _: builtins.elem username config.sydnix.users.users)
              self.homeConfigurations;

          home-manager.extraSpecialArgs = {
            utils = import ../lib/utils.nix {
              inherit config lib;
              pkgs = nixpkgs;
            };
            inherit inputs system;
          };
        })
      ];
    };
in
builtins.mapAttrs (dirName: _: mkHost dirName) (builtins.readDir ../hosts)
