{ 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 = { host, port, files, ... }: {
        name = host;
        value =
          let extra =
            if port != null then {
              locations."/" = {
                proxyPass = "http://localhost:${toString port}";
                proxyWebsockets = true;
              };
            }
            else {
              root = 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;
  };
}