{
  config,
  pkgs,
  lib,
  ...
}:

with lib;
let
  # We assume that all static pages are hosted on lindberg-webapps
  staticPages = pipe config.qois.static-page.pages [
    (mapAttrsToList (name: { domain, domainAliases, ... }: [ domain ] ++ domainAliases))
    flatten
    (map (name: {
      inherit name;
      value = "lindberg-webapps";
    }))
    listToAttrs
  ];
  defaultDomains = staticPages // {
    "cloud.qo.is" = "lindberg-nextcloud";

    "build.qo.is" = "lindberg-build";
    "gitlab-runner.qo.is" = "lindberg-build";
    "nixpkgs-cache.qo.is" = "lindberg-build";
    "attic.qo.is" = "lindberg-build";

    "vault.qo.is" = "lindberg-webapps";
    "git.qo.is" = "lindberg-webapps";

    "kokus.raphael.li" = "lindberg-rzimmermann";
    "auth.raphael.li" = "lindberg-rzimmermann";
    "toolia.raphael.li" = "lindberg-rzimmermann";
    "ha.raphael.li" = "lindberg-rzimmermann";
    "www.raphael.li" = "lindberg-rzimmermann";

    "vpn.qo.is" = "cyprianspitz-headscale";
  };
  getBackplaneIp = hostname: config.qois.meta.network.virtual.backplane.hosts.${hostname}.v4.ip;
  defaultHostmap =
    lib.pipe
      [
        "lindberg-nextcloud"
        "lindberg-build"
        "lindberg-webapps"
      ]
      [
        (map (name: {
          inherit name;
          value = getBackplaneIp name;
        }))
        lib.listToAttrs
      ];
  defaultExtraConfig =
    let
      headscalePort = toString 46084;
      rzimmermannIp = "10.247.0.113";
    in
    ''
      # lindberg-rzimmermann (uses send-proxy-v2)
      backend lindberg-rzimmermann-https
        mode tcp
        server s1 ${rzimmermannIp}:443 send-proxy-v2

      backend lindberg-rzimmermann-http
        mode http
        server s1 ${rzimmermannIp}:80

      # cyprianspitz headscale
      backend cyprianspitz-headscale-http
        mode http
        server s1 ${getBackplaneIp "cyprianspitz"}:${headscalePort}

      backend cyprianspitz-headscale-https
        mode tcp
        server s1 ${getBackplaneIp "cyprianspitz"}:${headscalePort}
    '';
  cfg = config.qois.loadbalancer;
in
{

  options.qois.loadbalancer = with lib; {
    enable = mkEnableOption "Enable services http+s loadbalancing";

    domains = mkOption {
      description = "Domain to hostname mappings";
      type = with lib.types; attrsOf str;
      default = defaultDomains;
    };

    hostmap = mkOption {
      description = "Hostname to IP mappings for TLS-TCP and http forwarding";
      type = with lib.types; attrsOf str;
      default = defaultHostmap;
    };

    extraConfig = mkOption {
      description = "Additional haproxy mapping configs. Amended to services.haproxy.config. Make sure indentations are correct.";
      type = types.nullOr types.lines;
      default = defaultExtraConfig;
    };

  };

  config =
    with lib;
    mkIf cfg.enable {

      networking.firewall.allowedTCPPorts = [
        80
        443
      ];

      services.haproxy =
        let
          domainMappingFile = pipe cfg.domains [
            (mapAttrsToList (host: backend: "${host} ${backend}"))
            concatLines
            (pkgs.writeText "haproxy_backend_map")
          ];
          genHttpBackend = hostName: ip: ''

            # Mapping for ${hostName}
            backend ${hostName}-https
              mode tcp
              server s1 ${ip}:443

            backend ${hostName}-http
              mode http
              server s1 ${ip}:80
          '';
          httpBackends = pipe cfg.hostmap [
            (mapAttrsToList genHttpBackend)
            concatLines
          ];
        in
        {
          enable = true;
          config = ''
            defaults
              mode http
              retries 3
              maxconn 2000
              timeout connect 5000
              timeout client 50000
              timeout server 50000

            frontend http
              mode http
              bind *:80
              use_backend %[req.hdr(host),lower,map_dom(${domainMappingFile})]-http

            frontend https
              bind *:443
              mode tcp
              tcp-request inspect-delay 5s
              tcp-request content accept if { req_ssl_hello_type 1 }

              use_backend %[req.ssl_sni,lower,map_dom(${domainMappingFile})]-https

            ## Generated Backends:
            ${httpBackends}

            ## extraConfig
            ${cfg.extraConfig}
          '';
        };
    };
}
