{
  config,
  lib,
  ...
}:
let
  cfg = config.khscodes.security.kanidm;
  secretFileForApplication = key: "/run/kanidm/${key}_secret";
  secretFiles = lib.attrsets.mapAttrsToList (
    key: value: secretFileForApplication key
  ) cfg.applications;
  vaultAgentTemplates = lib.attrsets.mapAttrsToList (key: value: {
    contents = ''
      {{- with secret "kanidm/data/apps/${key}" -}}
      {{ .Data.data.basic_secret }}
      {{- end -}}
    '';
    destination = secretFileForApplication key;
    perms = "0600";
    owner = "kanidm";
    group = "kanidm";
    reloadOrRestartUnits = [ "kanidm.service" ];
  }) cfg.applications;
  terranixModules = lib.attrsets.mapAttrsToList (
    key: value:
    { config, ... }:
    let
      sanitizedKey = lib.khscodes.sanitize-terraform-name (
        if value.terranixName == null then key else value.terranixName
      );
    in
    {
      resource.random_password."${sanitizedKey}_secret" = {
        length = 48;
        numeric = true;
        lower = true;
        upper = true;
        special = false;
      };
      resource.vault_kv_secret_v2."${sanitizedKey}_secret" = {
        mount = config.khscodes.vault.output.mount.kanidm.path;
        name = "apps/${key}";
        data_json = ''
          { "basic_secret": "''${ resource.random_password.${sanitizedKey}_secret.result }" }
        '';
      };
    }
  ) cfg.applications;
  systemsOauth2 = lib.attrsets.mapAttrs (key: value: {
    inherit (value) scopeMaps claimMaps allowInsecureClientDisablePkce;
    present = true;
    public = false;
    preferShortUsername = true;
    basicSecretFile = lib.mkIf (!bootstrapping) (secretFileForApplication key);
    originUrl = value.allowedRedirectUris;
    originLanding = value.landingUri;
    displayName = value.displayName;
  }) cfg.applications;
  kanidmApplication = lib.khscodes.mkSubmodule {
    description = "Kanidm application";
    options = {
      terranixName = lib.mkOption {
        type = lib.types.nullOr lib.types.str;
        default = null;
      };
      displayName = lib.mkOption {
        type = lib.types.str;
      };
      scopeMaps = lib.mkOption {
        type = lib.types.attrsOf (lib.types.listOf lib.types.str);
      };
      allowedRedirectUris = lib.mkOption {
        type = lib.types.listOf lib.types.str;
      };
      allowInsecureClientDisablePkce = lib.mkOption {
        type = lib.types.bool;
        default = false;
      };
      landingUri = lib.mkOption {
        type = lib.types.str;
      };
      claimMaps = lib.mkOption {
        type = lib.types.anything;
        default = { };
      };
    };
  };
  bootstrapping = config.khscodes."security.kaareskovgaard.net".bootstrap.enable;
in
{
  options.khscodes.security.kanidm.applications = lib.mkOption {
    type = lib.types.attrsOf kanidmApplication;
  };

  config = {
    # Allow the server to read the secrets for its own apps
    khscodes.infrastructure.vault-server-approle.policy."kanidm/data/apps/*" = {
      capabilities = [ "read" ];
    };
    # Don't add dependencies from bootstrapping when not bootstrapping.
    systemd.services.kanidm = lib.mkIf (!bootstrapping) {
      unitConfig = {
        ConditionPathExists = secretFiles;
      };
    };
    khscodes.services.vault-agent.templates = vaultAgentTemplates;
    khscodes.infrastructure.provisioning.post.modules = terranixModules ++ [
      {
        terraform.required_providers.random = {
          source = "hashicorp/random";
          version = "3.7.2";
        };
        provider.random = { };
      }
    ];
    # We cannot add oauth2 apps before the secrets for them are generated.
    services.kanidm.provision.systems.oauth2 = lib.mkIf (!bootstrapping) systemsOauth2;
  };
}
