{
  config,
  lib,
  inputs,
  ...
}:
let
  cfg = config.khscodes.infrastructure.vault-server-approle;
  vaultDomain = config.khscodes.infrastructure.openbao.domain;
in
{
  options.khscodes.infrastructure.vault-server-approle = {
    enable = lib.mkEnableOption "Enables creating an OpenBAO role for the server";
    stage = lib.mkOption {
      type = lib.types.enum [
        "pre"
        "post"
      ];
      description = "The provisioning stage that should include the provisioning. This should be pre for every server except the OpenBAO server itself";
      default = "pre";
    };
    path = lib.mkOption {
      type = lib.types.str;
      default = "approle";
      description = "Sets the path, as a terraform expression, for the approle to get created in. Not useful for most instances, but useful when doing bootstrapping, to establish a dependency.";
    };
    role_name = lib.mkOption {
      type = lib.types.str;
      description = "Name of the role being created";
      default = config.networking.fqdnOrHostName;
    };
    policy = lib.mkOption {
      type = lib.types.attrsOf (
        lib.khscodes.mkSubmodule {
          options = {
            capabilities = lib.mkOption {
              type = lib.types.listOf (
                lib.types.enum [
                  "create"
                  "update"
                  "patch"
                  "read"
                  "delete"
                  "list"
                ]
              );
            };
          };
          description = "Vault role policy";
        }
      );
    };
    stageModules = lib.mkOption {
      type = lib.types.listOf lib.types.anything;
      description = "Extra modules to add to the configured stage";
      default = [ ];
    };
  };

  config = lib.mkIf cfg.enable {
    khscodes.services.read-vault-auth-from-userdata.enable = cfg.stage == "pre";
    khscodes.services.vault-agent.enable = true;
    khscodes.infrastructure.provisioning.${cfg.stage} = {
      modules = [
        (
          { config, lib, ... }:
          {
            imports = [ inputs.self.terranixModules.vault ];
            output = lib.mkIf (cfg.stage == "post") {
              role-id = {
                value = config.khscodes.vault.output.approle_auth_backend_role.${cfg.role_name}.role_id;
                sensitive = false;
              };
              secret-id-wrapped = {
                value =
                  config.khscodes.vault.output.approle_auth_backend_role_secret_id.${cfg.role_name}.wrapping_token;
                sensitive = true;
              };
            };
            khscodes.vault = {
              enable = true;
              domain = vaultDomain;
              approle_auth_backend_role.${cfg.role_name} = {
                backend = cfg.path;
                role_name = cfg.role_name;
                # Secret IDs never expire, to allow vault agent to restart without issues.
                # TODO: Look into doing this in a better way going forward, such that this won't
                #       be an issue under normal circumstances, but vault-agents (or instances)
                #       being offline for long periods of time should invalidate the secret id's.
                secret_id_ttl = 0;
                secret_id_num_uses = 0;
                token_ttl = 20 * 60;
                token_max_ttl = 30 * 60;
                token_policies = [ cfg.role_name ];
              };
              approle_auth_backend_role_secret_id.${cfg.role_name} = {
                backend = cfg.path;
                # Not hardcoding the role name here, as reading it like this will create a dependency
                # on the role being created first, which is needed.
                role_name = config.khscodes.vault.output.approle_auth_backend_role.${cfg.role_name}.role_name;
                wrapping_ttl = 30 * 60;
                num_uses = 0;

                # This should simply mean that we never attempt to recreate the secret id, as we don't want a rerun of the
                # provisioning to invalidate the existing secret id, nor recreate the entire server.
                with_wrapped_accessor = true;
                lifecycle = {
                  ignore_changes = [
                    "num_uses"
                    "ttl"
                  ];
                };
              };
              policy.${cfg.role_name} = {
                name = cfg.role_name;
                policy = lib.strings.concatStringsSep "\n\n" (
                  lib.lists.map (
                    { name, value }:
                    ''
                      path "${name}" {
                        capabilities = ${builtins.toJSON value.capabilities}
                      }
                    ''
                  ) (lib.attrsToList cfg.policy)
                );
              };
            };
          }
        )
      ] ++ cfg.stageModules;
    };
    # I can only provide the user data if the stage is pre (along with the instance creation)
    # Also I should probably find a way of injecting this in a nicer way than this mess.
    khscodes.infrastructure.provisioning.instanceUserData = lib.mkIf (cfg.stage == "pre") {
      VAULT_ROLE_ID = "\${ vault_approle_auth_backend_role.${lib.khscodes.sanitize-terraform-name cfg.role_name}.role_id }";
      VAULT_SECRET_ID_WRAPPED = "\${ vault_approle_auth_backend_role_secret_id.${lib.khscodes.sanitize-terraform-name cfg.role_name}.wrapping_token }";
    };
  };
}
