{
  config,
  pkgs,
  lib,
  ...
}:
with lib;
let
  cfg = config.qois.vpn-server;
  cfgLoadbalancer = config.qois.loadbalancer;
  defaultDnsRecords =
    (mapAttrs (
      name: value: mkIf (cfgLoadbalancer.hostmap ? ${value}) cfgLoadbalancer.hostmap.${value}
    ) cfgLoadbalancer.domains)
    // {
      "vpn.qo.is" = config.services.headscale.address;
    };
in
{

  options.qois.vpn-server = {
    enable = mkEnableOption "Enable vpn server services";
    domain = mkOption {
      description = "Domain for the VPN admin server";
      type = types.str;
      default = "vpn.qo.is";
    };
    dnsRecords = mkOption {
      description = "DNS records to add to Hosts";
      type = with types; attrsOf str;
      default = defaultDnsRecords;
    };
    wheelUsers = mkOption {
      description = "Usernames that can change configurations";
      type = with types; listOf str;
      default = [ ];
    };
  };

  config = mkIf cfg.enable ({

    environment.systemPackages = [ pkgs.headscale ];

    # We bind to the backplane vpn IP, so wait for the wireguard net to be available
    systemd.services.headscale.after = [ "wireguard-wg-backplane.service" ];

    qois.backup-client.includePaths =
      with config.services.headscale.settings;
      (
        [
          database.sqlite.path
          derp.server.private_key_path
          noise.private_key_path
        ]
        ++ derp.paths
      );

    networking.firewall.checkReversePath = "loose";
    networking.firewall.allowedUDPPorts = [
      41641
    ];
    services.headscale =
      let
        vnet = config.qois.meta.network.virtual;
        vpnNet = vnet.vpn;
        vpnNetPrefix = "${vpnNet.v4.id}/${toString vpnNet.v4.prefixLength}";
        backplaneNetPrefix = "${vnet.backplane.v4.id}/${builtins.toString vnet.backplane.v4.prefixLength}";
      in
      {
        enable = true;
        address = vnet.backplane.hosts.cyprianspitz.v4.ip;
        port = 46084;
        settings = {
          server_url = "https://${cfg.domain}:443";

          tls_letsencrypt_challenge_type = "TLS-ALPN-01";
          tls_letsencrypt_hostname = vpnNet.domain;

          dns = {
            base_domain = vpnNet.domain;
            magic_dns = true;
            nameservers.global = [ vnet.backplane.hosts.calanda.v4.ip ];
            search_domains = [
              # vpnNet.domain # First by default with magic_dns
              vnet.backplane.domain
            ];
            extra_records = pipe cfg.dnsRecords [
              attrsToList
              (map (val: val // { type = "A"; }))
            ];
          };

          ip_prefixes = [ vpnNetPrefix ];

          policy =
            let
              # Note: headscale has limited acl support currently. This might change in the future.
              aclPolicy = {
                hosts = {
                  "clients" = vpnNetPrefix;
                };
                groups = {
                  "group:wheel" = cfg.wheelUsers;
                };
                tagOwners = {
                  "tag:srv" = [ "srv" ]; # srv tag ist owned by srv user
                };
                autoApprovers = {
                  exitNode = [
                    "tag:srv"
                    "group:wheel"
                  ];
                  routes = {
                    ${backplaneNetPrefix} = [ "tag:srv" ];
                  };
                };

                acls = [
                  # Allow all communication from and to srv tagged hosts
                  {
                    action = "accept";
                    src = [
                      "tag:srv"
                      "srv"
                    ];
                    dst = [ "*:*" ];
                  }
                  {
                    action = "accept";
                    src = [ "*" ];
                    dst = [
                      "tag:srv:*"
                      "srv:*"
                    ];
                  }

                  # Allow access to all connected hosts for wheels
                  {
                    action = "accept";
                    src = [ "group:wheel" ];
                    dst = [ "*:*" ];
                  }
                ];
              };
            in
            {
              mode = "file";
              path = pkgs.writeTextFile {
                name = "acls";
                text = builtins.toJSON aclPolicy;
              };
            };
        };
      };
  });
}
