{ pkgs, lib, config, inputs, hosts, info, ... }:
let
  domain = "sourcephile.fr";
  domainID = lib.replaceStrings [ "." ] [ "_" ] domain;
  inherit (config) networking;
  inherit (config.services) knot;
  inherit (config.users) users groups;
  zoneData =
    # TODO: increase the TTL once things have settled down
    ''
      $ORIGIN ${domain}.
      $TTL 500

      ; SOA (Start Of Authority)
      @ SOA ns root (
        ${toString inputs.self.lastModified} ; Serial number
        24h   ; Refresh
        15m   ; Retry
        1000h ; Expire (1000h)
        1d    ; Negative caching
      )

      ; NS (Name Server)
      @ NS ns
      ;@ NS ${info.gandi.dns.secondary.ns.name}.
      i NS ns
      whoami4 NS ns.whoami4
      ns.whoami4 A ${hosts.mermet._module.args.ipv4}

      ; A (DNS -> IPv4)
      @            A ${hosts.mermet._module.args.ipv4}
      mermet       A ${hosts.mermet._module.args.ipv4}
      autoconfig   A ${hosts.mermet._module.args.ipv4}
      calibre      A ${hosts.mermet._module.args.ipv4}
      doc          A ${hosts.mermet._module.args.ipv4}
      git          A ${hosts.mermet._module.args.ipv4}
      imap         A ${hosts.mermet._module.args.ipv4}
      mail         A ${hosts.mermet._module.args.ipv4}
      mails        A ${hosts.mermet._module.args.ipv4}
      news         A ${hosts.mermet._module.args.ipv4}
      public-inbox A ${hosts.mermet._module.args.ipv4}
      ns           A ${hosts.mermet._module.args.ipv4}
      pop          A ${hosts.mermet._module.args.ipv4}
      smtp         A ${hosts.mermet._module.args.ipv4}
      submission   A ${hosts.mermet._module.args.ipv4}
      www          A ${hosts.mermet._module.args.ipv4}
      croc         A ${hosts.mermet._module.args.ipv4}
      stun         A ${hosts.mermet._module.args.ipv4}
      turn         A ${hosts.mermet._module.args.ipv4}
      whoami       A ${hosts.mermet._module.args.ipv4}
      code          A ${hosts.mermet._module.args.ipv4}
      miniflux      A ${hosts.mermet._module.args.ipv4}

      ; MX (Mail eXchange)
      @ 500 MX 5 mail

      ; CNAME (Canonical Name)
      openconcerto     CNAME losurdo
      xmpp             CNAME mermet
      salons           CNAME mermet
      tmp              CNAME mermet
      proxy65          CNAME mermet
      cryptpad         CNAME losurdo
      cryptpad-api     CNAME losurdo
      cryptpad-files   CNAME losurdo
      cryptpad-sandbox CNAME losurdo
      mumble           CNAME mermet
      freeciv          CNAME losurdo
      nix-serve        CNAME losurdo
      nix-extracache   CNAME losurdo
      nix-localcache   CNAME lan.losurdo
      sftp             CNAME losurdo
      radicle-mermet   CNAME mermet
      radicle          CNAME mermet
      radicle-explorer CNAME radicle

      ; DMARC (Domain-based Message Authentication, Reporting and Conformance)
      _dmarc 3600 IN TXT "v=DMARC1; p=none; pct=100; rua=mailto:root+dmarc+aggregate@sourcephile.fr; ruf=mailto:root+dmarc+forensic@sourcephile.fr"

      ; SPF (Sender Policy Framework)
      @ 3600 IN TXT "v=spf1 mx ip4:${hosts.mermet._module.args.ipv4} -all"

      ; SRV (SeRVice)
      _git._tcp.git             18000 IN SRV 0 0 9418 git
      _stun._udp                18000 IN SRV 0 5 3478 stun
      _xmpp-client._tcp         18000 IN SRV 0 5 5222 xmpp
      _xmpp-server._tcp         18000 IN SRV 0 5 5269 xmpp
      _xmpp-server._tcp.salons  18000 IN SRV 0 5 5269 xmpp
      _xmpps-client._tcp        18000 IN SRV 0 5 5223 xmpp
      _xmpps-server._tcp        18000 IN SRV 0 5 5270 xmpp
      _xmpps-server._tcp.salons 18000 IN SRV 0 5 5270 xmpp

      ; CAA (Certificate Authority Authorization)
      ; DOC: https://blog.qualys.com/ssllabs/2017/03/13/caa-mandated-by-cabrowser-forum
      @ CAA 128 issue "letsencrypt.org; validationmethods=dns-01"
    '';
  # Incorrect:
  # accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/78014180
in
{
  services.knot.settingsFreeform = {
    remote.ns_iodine.address = "127.0.0.1@1053";
    acl."acl_localhost_acme_${domainID}" = {
      address = "127.0.0.1";
      action = "update";
      update-owner = "name";
      update-owner-match = "equal";
      update-owner-name = [ "_acme-challenge" ];
      update-type = [ "TXT" ];
    };
    acl."acl_tsig_acme_${domainID}" = {
      key = "acme_${domainID}";
      action = "update";
      update-owner = "name";
      update-owner-match = "equal";
      update-owner-name = [ "_acme-challenge" ];
      update-type = [ "TXT" ];
    };
    acl."acl_tsig_losurdo_${domainID}" = {
      key = "losurdo_${domainID}";
      action = "update";
      update-owner = "name";
      update-owner-match = "equal";
      update-owner-name = [ "losurdo" "lan.losurdo" ];
      update-type = [ "A" "AAAA" ];
    };
    acl."acl_lebureau_${domainID}" = {
      address =
        info.lebureau.dns.secondary.transfer.ipv4 ++
        info.lebureau.dns.secondary.transfer.ipv6;
      action = "transfer";
      key = "lebureau_${domainID}";
    };
    mod-dnsproxy.proxy_iodine = {
      remote = "ns_iodine";
      fallback = "off";
    };
    remote."secondary_lebureau_${domainID}" = {
      address = map (x: "${x}@53")
        (info.lebureau.dns.secondary.transfer.ipv4 ++
          info.lebureau.dns.secondary.transfer.ipv6);
      key = "lebureau_${domainID}";
    };
    zone."${domain}" = {
      file = "${domain}.zone";
      serial-policy = "increment";
      semantic-checks = true;
      notify = [
        "secondary_lebureau_${domainID}"
      ];
      acl = [
        "acl_localhost_acme_${domainID}"
        "acl_tsig_acme_${domainID}"
        "acl_tsig_losurdo_${domainID}"
        "acl_lebureau_${domainID}"
      ];
      dnssec-signing = true;
      dnssec-policy = "ed25519";
    };
    #zone."i.${domain}" = {
    #  module = "mod-dnsproxy/proxy_iodine";
    #};
    zone."whoami4.${domain}" = {
      module = "mod-whoami";
      file = pkgs.writeText "whoami4.zone" ''
        $TTL 1
        @ SOA ns root.${domain}. (
          0     ; SERIAL
          86400 ; REFRESH
          86400 ; RETRY
          86400 ; EXPIRE
          1 ; MINIMUM
        )
        $TTL 86400
        @ NS ns
        ns A ${hosts.mermet._module.args.ipv4}
      '';
    };
  };
  services.knot = {
    keyFiles = [
      "/run/credentials/knot.service/${domain}.acme.conf"
      # Generated with: keymgr -t losurdo_${domainID}
      "/run/credentials/knot.service/losurdo.conf"
      # Generated with: keymgr -t lebureau_${domainID}
      "/run/credentials/knot.service/${domain}.lebureau.conf"
    ];
  };
  systemd.services.knot = {
    serviceConfig = {
      ExecStartPre = [
        ''
          +${pkgs.coreutils}/bin/install -D -o ${users.knot.name} -g ${groups."knot".name} -m 700 \
           ${pkgs.writeText "${domain}.zone" zoneData} \
           /var/lib/knot/zones/${domain}.zone
        ''
      ];
      LoadCredentialEncrypted = [
        "${domain}.acme.conf:${builtins.path { path = ./. + "/${domain}/acme.conf.cred"; }}"
        "${domain}.lebureau.conf:${builtins.path { path = ./. + "/${domain}/lebureau.conf.cred"; }}"
        "losurdo.conf:${builtins.path { path = ./. + "/${domain}/losurdo.conf.cred"; }}"
      ];
    };
  };
  networking.nftables.ruleset = ''
    table inet filter {
      set output-net-knot-ipv4 { type ipv4_addr; elements = { ${lib.concatStringsSep ", " info.lebureau.dns.secondary.transfer.ipv4} }; }
      set output-net-knot-ipv6 { type ipv6_addr; elements = { ${lib.concatStringsSep ", " info.lebureau.dns.secondary.transfer.ipv6} }; }
    }
  '';
  /* Useless since the zone is public
    services.unbound.settings = {
    stub-zone = {
    name = domain;
    stub-addr = "127.0.0.1@5353";
    };
    };
    '';
  */
}