{ pkgs, lib, config, info, ... }:
let
  inherit (lib) types;
  inherit (config.services) knot;
  inherit (config.users) users groups;
  settingsFormat = pkgs.formats.yaml { };
in
{
  imports = [
    knot/autogeree.net.nix
    knot/sourcephile.fr.nix
  ];
  options.services.knot = {
    # WARNING: multiple settings do not merge yet
    # https://github.com/NixOS/nixpkgs/pull/81460#pullrequestreview-1793815097
    settingsFreeform = lib.mkOption {
      type = types.submodule {
        freeformType = settingsFormat.type;
      };
      default = { };
      description = "";
    };
    zones = lib.mkOption {
      default = { };
      type = types.attrsOf (types.submodule ({ ... }: {
        #config.domain = lib.mkDefault name;
        options = {
          data = lib.mkOption {
            type = types.nullOr types.lines;
          };
        };
      }));
    };
  };
  config = {
    systemd.services.knot.serviceConfig.ExecStartPre =
      lib.mapAttrsToList
        (domain: { data, ... }: ''
          +${pkgs.coreutils}/bin/install -D -o ${users.knot.name} -g ${groups."knot".name} -m 700 \
           ${pkgs.writeText "${domain}.zone" data} \
           /var/lib/knot/zones/${domain}.zone
        '')
        knot.zones;
    /*
      systemd.services.knot.postStart = lib.mkAfter ''
      PATH="/run/current-system/sw/bin:$PATH"
      knotc zone-freeze ${domain}.
      while ! knotc zone-status ${domain}. +freeze | grep -q 'freeze: yes'; do sleep 1; done
      knotc zone-flush ${domain}.
      install -o knot -g knot -m 700 ${zone} /var/lib/knot/signed/${domain}.zone
      knotc zone-reload ${domain}.
      knotc zone-thaw ${domain}.
      '';
    */
    networking.nftables.ruleset = ''
      table inet filter {
        chain input-net {
          meta l4proto { udp, tcp } th dport domain counter accept comment "knot: DNS"
        }
        set output-net-knot-ipv4 { type ipv4_addr; }
        set output-net-knot-ipv6 { type ipv6_addr; }
        chain output-net {
          skuid ${users.knot.name} \
            meta l4proto { udp, tcp } th dport domain \
            ip daddr @output-net-knot-ipv4 \
            counter accept \
            comment "knot: DNS notify"
          skuid ${users.knot.name} \
            meta l4proto { udp, tcp } th dport domain \
            ip6 daddr @output-net-knot-ipv6 \
            counter accept \
            comment "knot: DNS notify"
        }
      }
    '';
    services.knot = {
      enable = true;
      extraArgs = [ "-v" ];
      # https://www.knot-dns.cz/docs/2.6/html/reference.html
      settingsFreeform = {
        server.listen = [
          # Listen on localhost to allow only there
          # dynamic updates for ACME challenges.
          "127.0.0.1@5353"
        ];
        template.default = {
          dnssec-signing = false;
          # move databases below the state directory, because they need to be writable
          storage = "/var/lib/knot/zones";
          # Input-only zone files
          # https://www.knot-dns.cz/docs/2.8/html/operation.html#example-3
          # prevents modification of the zonefiles, since the zonefiles are immutable
          #zonefile-sync: -1
          zonefile-load = "difference";
          journal-content = "changes";
          global-module = "mod-rrl/default";
        };
        mod-rrl.default = {
          rate-limit = 200;
          slip = 2;
        };
        database = {
          journal-db = "/var/lib/knot/journal";
          kasp-db = "/var/lib/knot/kasp";
          timer-db = "/var/lib/knot/timer";
        };
        log.syslog.any = "info";
        remote.local_resolver.address = "127.0.0.1@53";
        remote.secondary_gandi.address = "${info.gandi.dns.secondary.transfer.ipv4}@53";
        remote.secondary_muarf.address = "78.192.65.63@53";
        submission.dnssec_validating_resolver = {
          parent = "local_resolver";
        };
        policy.rsa = {
          single-type-signing = false;
          ksk-shared = false;
          algorithm = "RSASHA256";
          ksk-size = 4096;
          zsk-size = 2048;
          zsk-lifetime = "30d";
          ksk-lifetime = "365d";
          ksk-submission = "dnssec_validating_resolver";
        };
        policy.ed25519 = {
          single-type-signing = false;
          ksk-shared = false;
          algorithm = "ED25519";
          ksk-size = 256;
          zsk-size = 256;
          zsk-lifetime = "30d";
          ksk-lifetime = "365d";
          cds-cdnskey-publish = "always";
          ksk-submission = "dnssec_validating_resolver";
        };
        acl.acl_gandi = {
          address = info.gandi.dns.secondary.transfer.ipv4;
          action = "transfer";
        };
        acl.acl_muarf = {
          address = "78.192.65.63";
          action = "transfer";
        };
      };
      settings = knot.settingsFreeform;
    };
  };
}