{ self, nixpkgs, agenix, bij, chaosevents, fernglas, fieldpoc, nixos-exporter, solid-xmpp-alarm, sops-nix, ... }@inputs:

rec {
  generateNixosSystem = {
    name,
    system ? "x86_64-linux",
    group ? null,
    modules ? [],
  }: let
    localNixpkgs = nixpkgs.lib.attrByPath [ "nixpkgs-${name}" ] nixpkgs inputs;
  in localNixpkgs.lib.nixosSystem {
    system = system;
    modules = modules ++ [
      ({ ... }: {
        /*
          Make the contents of the flake availiable to modules.
          Useful for having the monitoring server scraping the
          target config from all other servers automatically.
        */
        _module.args = {
          inputs = inputs;
          _nixfiles = self;
        };
      })
      ../configuration/common
      ../users/clerie
      ({ ... }: {
        nixpkgs.overlays = [
          self.overlays.clerie
          (_: _: {
            inherit (agenix.packages."x86_64-linux")
              agenix;
            inherit (bij.packages."${system}")
              bij;
            inherit (chaosevents.packages."x86_64-linux")
              chaosevents;
           })
        ];
        clerie.monitoring = nixpkgs.lib.attrsets.optionalAttrs (group != null) { serviceLevel = group; };
      })
      agenix.nixosModules.default
      fernglas.nixosModules.default
      fieldpoc.nixosModules.default
      nixos-exporter.nixosModules.default
      solid-xmpp-alarm.nixosModules.solid-xmpp-alarm
      sops-nix.nixosModules.sops
      (../hosts + "/${name}/configuration.nix")
      # Automatically load secrets from the hosts secrets directory
      ({ lib, ... }: let
        secretsPath = ../hosts + "/${name}/secrets";
      in {
        age.secrets = lib.mapAttrs' (filename: _: lib.nameValuePair (lib.removeSuffix ".age" filename) {
          file = secretsPath + "/${filename}";
        }) (lib.filterAttrs (name: type: (type == "regular") && (lib.hasSuffix ".age" name) ) (if builtins.pathExists secretsPath then builtins.readDir secretsPath else {}));
      })
      # Automatically load secrets from sops file for host
      ({ config, lib, ... }: {
        sops.defaultSopsFile = ../hosts + "/${name}/secrets.json";
        sops.secrets = let
          secretFile = config.sops.defaultSopsFile;
          secretNames = builtins.filter (name: name != "sops") (builtins.attrNames (builtins.fromJSON (builtins.readFile secretFile)));
          secrets = if builtins.pathExists secretFile then
            lib.listToAttrs (builtins.map (name: lib.nameValuePair name {}) secretNames)
          else
            {};
        in
          secrets;
      })
    ];
  };

  mapToNixosConfigurations = hosts: builtins.mapAttrs (name: host: generateNixosSystem host) hosts;

  generateColmenaHost = name: hostSystem: {
    deployment = {
      targetHost = hostSystem.config.networking.fqdn;
      targetUser = null;
      tags = let
        group = nixpkgs.lib.attrByPath [ "clerie" "monitoring" "serviceLevel" ] null hostSystem.config;
      in nixpkgs.lib.lists.optional (group != null) group;
    };
    nixpkgs.system = hostSystem.config.nixpkgs.system;
    imports = hostSystem._module.args.modules;
    deployment.allowLocalDeployment = builtins.any (n: n == name) [ "osmium" ];
  };

  mapToColmenaHosts = hosts: builtins.mapAttrs (generateColmenaHost) hosts;

  buildHosts = hosts: builtins.mapAttrs (name: host: host.config.system.build.toplevel) (nixpkgs.lib.filterAttrs (name: host: (builtins.substring 0 1 name) != "_") hosts);
}