{ config, lib, ... }:
let
  cfg = config.satellite.nginx;
in
{
  options.satellite.nginx = {
    domain = lib.mkOption {
      description = "Root domain to use as a default for configurations.";
      type = lib.types.str;
      default = config.satellite.dns.domain;
    };

    at = lib.mkOption {
      description = "Per-subdomain nginx configuration";
      type = lib.types.attrsOf (
        lib.types.submodule (
          { name, config, ... }:
          {
            options.subdomain = lib.mkOption {
              description = ''
                Subdomain to use for host generation.
                Only required if `host` is not set manually.
              '';
              type = lib.types.str;
              default = name;
            };

            options.host = lib.mkOption {
              description = "Host to route requests from";
              type = lib.types.str;
            };

            config.host = "${config.subdomain}.${cfg.domain}";

            options.url = lib.mkOption {
              description = "External https url used to access this host";
              type = lib.types.str;
            };

            config.url = "https://${config.host}";

            options.port = lib.mkOption {
              description = "Port to proxy requests to";
              type = lib.types.nullOr lib.types.port;
              default = null;
            };

            options.files = lib.mkOption {
              description = "Path to serve files from";
              type = lib.types.nullOr lib.types.path;
              default = null;
            };
          }
        )
      );
      default = { };
    };
  };

  config = {
    assertions =
      let
        assertSingleTarget = config: {
          assertion = (config.port == null) == (config.files != null);
          message = ''
            Precisely one of the options 'satellite.nginx.at.${config.subdomain}.port'
            and 'satellite.nginx.at.${config.subdomain}.files' must be specified.
          '';
        };
      in
      lib.mapAttrsToList (_: assertSingleTarget) cfg.at;

    services.nginx.virtualHosts =
      let
        mkNginxConfig = args: {
          name = args.host;
          value =
            let
              extra =
                if args.port != null then
                  {
                    locations."/" = {
                      proxyPass = "http://localhost:${toString args.port}";
                      proxyWebsockets = true;
                    };
                  }
                else
                  { root = args.files; };
            in
            {
              enableACME = true;
              acmeRoot = null;
              forceSSL = true;
            }
            // extra;
        };
      in
      lib.attrsets.mapAttrs' (_: mkNginxConfig) cfg.at;

    satellite.dns.records =
      let
        mkDnsRecord =
          { subdomain, ... }:
          {
            type = "CNAME";
            zone = cfg.domain;
            at = subdomain;
            to = config.networking.hostName;
          };
      in
      lib.attrsets.mapAttrsToList (_: mkDnsRecord) cfg.at;
  };
}