{ pkgs, lib, config, ... }:
let
    cfg = config.services.dyndns;
in
{
    options.services.dyndns = {
        enable = lib.mkEnableOption "Update DNS AAAA records via dyndns";

        interface = lib.mkOption {
            type = lib.types.str;
            description = "Identifier of the network interface to use";
        };

        domains = lib.mkOption {
            type = lib.types.listOf lib.types.str;
            description = "Domain names to update";
        };

        server = lib.mkOption {
            type = lib.types.str;
            description = "DynDNS server name";
        };

        username = lib.mkOption {
            type = lib.types.str;
            description = "Username for DynDNS updates";
        };

        passwordFile = lib.mkOption {
            type = lib.types.str;
            description = "File containing the DynDNS password";
        };
    };

    config = lib.mkIf cfg.enable {
        systemd.services.dyndns = let
            stateDirectory = "dyndns";
            homeDirectory = "/var/lib/${stateDirectory}";
        in
        {
            enable = true;
            after = [ "network.target" ];
            unitConfig = {
                Description = "Update AAAA records via DynDNS";
            };
            serviceConfig = {
                DynamicUser = true;
                PrivateTmp = true;
                PrivateDevices = true;
                ProtectSystem = true;
                ProtectHome = true;
                NoNewPrivileges = true;
                ReadWriteDirectories = [ homeDirectory ];
                StateDirectory = stateDirectory;
            };
            script = ''
                #!${pkgs.bash}/bin/bash

                set -eu

                interface="${cfg.interface}"
                dyndns_server="${cfg.server}"
                dyndns_user="${cfg.username}"
                dyndns_password="$(cat "${cfg.passwordFile}")"

                new_ip=$(${pkgs.iproute}/bin/ip -6 a show scope global -temporary dev "$interface" | ${pkgs.gnused}/bin/sed -n -E 's/^\ *inet6\ (2001(:[0-9a-f]+)+).*$/\1/p' | head -1)

                if [ -z "$new_ip" ] ; then
                    echo "Could not determine IP address."
                    exit 1
                fi

                for host in ${builtins.concatStringsSep " " cfg.domains} ; do
                    echo "Checking $host."

                    state_file="${homeDirectory}/current_ipv6_$host"
                    if [ ! -f "$state_file" ] ; then
                        echo "No state file found, determining currently set IP via DNS query."
                        ${pkgs.dig}/bin/dig aaaa +short "$host" > "$state_file"
                    fi

                    current_ip=$(cat "$state_file")

                    if [ -z "$current_ip" ] ; then
                        echo "Could not determine current AAAA record."
                        exit 1
                    fi

                    if [ "$current_ip" = "$new_ip" ] ; then
                        echo "Current AAAA record is already $current_ip, no update needed."
                        continue
                    fi

                    echo "Updating IP to $new_ip."
                    ${pkgs.curl}/bin/curl "https://$dyndns_user:$dyndns_password@$dyndns_server/?hostname=$host&myip=$new_ip"

                    echo "$new_ip" > "$state_file"
                done
            '';
        };

        systemd.timers.dyndns = {
            description = "Timer for triggering DynDNS updates";
            wantedBy = [ "timers.target" ];
            timerConfig = {
                OnBootSec = "2min";
                OnUnitActiveSec = "20min";
                Unit = "dyndns.service";
            };
        };
    };
}