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

with lib;
let
  cfg = config.fudo.secure-dns-proxy;

  fudo-lib = import ../fudo-lib.nix { lib = lib; };

in {
  options.fudo.secure-dns-proxy = with types; {
    enable =
      mkEnableOption "Enable a DNS server using an encrypted upstream source.";

    listen-port = mkOption {
      type = port;
      description = "Port on which to listen for DNS queries.";
      default = 53;
    };

    upstream-dns = mkOption {
      type = listOf str;
      description = ''
        The upstream DNS services to use, in a format useable by dnsproxy.

        See: https://github.com/AdguardTeam/dnsproxy
      '';
      default = [ "https://cloudflare-dns.com/dns-query" ];
    };

    bootstrap-dns = mkOption {
      type = str;
      description =
        "A simple DNS server from which HTTPS DNS can be bootstrapped, if necessary.";
      default = "1.1.1.1";
    };

    listen-ips = mkOption {
      type = listOf str;
      description = "A list of local IP addresses on which to listen.";
      default = [ "0.0.0.0" ];
    };

    allowed-networks = mkOption {
      type = nullOr (listOf str);
      description =
        "List of networks with which this job is allowed to communicate.";
      default = null;
    };

    user = mkOption {
      type = str;
      description = "User as which to run secure DNS proxy.";
      default = "secure-dns-proxy";
    };

    group = mkOption {
      type = str;
      description = "Group as which to run secure DNS proxy.";
      default = "secure-dns-proxy";
    };
  };

  config = mkIf cfg.enable (let
    upgrade-perms = cfg.listen-port <= 1024;
  in {
    users = mkIf upgrade-perms {
      users = {
        ${cfg.user} = {
          isSystemUser = true;
          group = cfg.group;
        };
      };

      groups = {
        ${cfg.group} = {
          members = [ cfg.user ];
        };
      };
    };

    fudo.system.services.secure-dns-proxy = {
      description = "DNS Proxy for secure DNS-over-HTTPS lookups.";
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];
      privateNetwork = false;
      requiredCapabilities = mkIf upgrade-perms [ "CAP_NET_BIND_SERVICE" ];
      restartWhen = "always";
      addressFamilies = [ "AF_INET" "AF_INET6" ];
      networkWhitelist = cfg.allowed-networks;
      user = mkIf upgrade-perms cfg.user;
      group = mkIf upgrade-perms cfg.group;

      execStart = let
        upstreams = map (upstream: "-u ${upstream}") cfg.upstream-dns;
        upstream-line = concatStringsSep " " upstreams;
        listen-line =
          concatStringsSep " " (map (listen: "-l ${listen}") cfg.listen-ips);
      in "${pkgs.dnsproxy}/bin/dnsproxy -p ${
        toString cfg.listen-port
      } ${upstream-line} ${listen-line} -b ${cfg.bootstrap-dns}";
    };
  });
}