{ depot, lib, config, ... }:

let
  inherit (lib) mkOption nameValuePair mapToAttrs types mkEnableOption mapAttrs' filterAttrs mkMerge mapAttrsToList concatStringsSep mkBefore;

  minutes = m: m * 60;

  serversType = types.attrsOf (types.submodule ({ name, ... }: {
    options = {
      enable = mkOption {
        type = types.bool;
        default = true;
      };

      resourceName = mkOption {
        type = types.str;
        default = "server_${name}";
        internal = true;
      };

      extraPolicies = mkOption {
        type = with types; listOf str;
        default = [];
      };

      apps = mkOption {
        type = with types; listOf str;
        default = [];
      };

      appPolicies = mkOption {
        # Server-specific app policies.
        type = with types; attrsOf lines;
        default = {};
      };

      userPolicies = mkOption {
        # Server-specific user policies.
        type = with types; attrsOf lines;
        default = {};
      };

      hostnames = mkOption {
        type = with types; listOf str;
        default = [
          "${name}.as205479.net"
          "${name}.blade.as205479.net"
          "${name}.int.as205479.net"
        ];
      };

      policy = mkOption {
        type = types.lines;
        default = ''
          path "ssh-host/sign/${name}" {
            capabilities = ["update"]
            allowed_parameters = {
              "cert_type" = ["host"]
              "public_key" = []
              "valid_principals" = []
            }
          }
        '';
      };
    };

    config.apps = mkBefore [ "deployer" ];
  }));

  cfg = config.my.enabledServers;
in {
  options = {
    my.servers = mkOption {
      type = serversType;
    };

    my.enabledServers = mkOption {
      internal = true;
      readOnly = true;
      default = filterAttrs (n: v: v.enable) config.my.servers;
      type = serversType;
    };
  };

  config.my.servers = mapToAttrs (name: nameValuePair name {}) (builtins.attrNames depot.ops.nixos.systemConfigs);

  config.resource = mkMerge (mapAttrsToList (serverName: serverCfg: mkMerge ([{
    vault_policy.${serverCfg.resourceName} = {
      name = "server/${serverName}";
      inherit (serverCfg) policy;
    };

    vault_approle_auth_backend_role.${serverCfg.resourceName} = {
      backend = "\${vault_auth_backend.approle.path}";
      role_name = serverName;
      role_id = serverName;
      secret_id_num_uses = 0;
      token_ttl = minutes 20;
      token_max_ttl = minutes 30;
      token_policies =
        ["default" "server" "server-user" "\${vault_policy.${serverCfg.resourceName}.name}"]
        ++ serverCfg.extraPolicies
        ++ (map (name: "\${vault_policy.app_${name}.name}") serverCfg.apps)
        ++ (map (name: "\${vault_policy.server_${serverCfg.resourceName}_app_${name}.name}") (builtins.attrNames serverCfg.appPolicies))
        ++ (map (name: "\${vault_policy.server_${serverCfg.resourceName}_user_${name}.name}") (builtins.attrNames serverCfg.userPolicies));
    };

    vault_identity_entity.${serverCfg.resourceName} = {
      name = serverName;
      metadata.server = serverName;
    };

    vault_identity_entity_alias.${serverCfg.resourceName} = {
      name = serverName;
      mount_accessor = "\${vault_auth_backend.approle.accessor}";
      canonical_id = "\${vault_identity_entity.${serverCfg.resourceName}.id}";
    };

    vault_ssh_secret_backend_role.${serverCfg.resourceName} = {
      name = serverName;
      backend = "\${vault_mount.ssh-host.path}";
      key_type = "ca";
      allow_host_certificates = true;
      allow_bare_domains = true;
      allowed_domains = concatStringsSep "," serverCfg.hostnames;
      ttl = 7 * 24 * 60 * 60;
      max_ttl = 7 * 24 * 60 * 60;
    };
  }]
  ++ mapAttrsToList (appName: policy: {
    vault_policy.${"server_${serverCfg.resourceName}_app_${appName}"} = {
      name = "server/${serverName}/app/${appName}";
      inherit policy;
    };
  }) serverCfg.appPolicies
  ++ mapAttrsToList (userName: policy: {
    vault_policy.${"server_${serverCfg.resourceName}_user_${userName}"} = {
      name = "server/${serverName}/user/${userName}";
      inherit policy;
    };
  }) serverCfg.userPolicies)) cfg);
}