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

with lib;

let
  pppInterface = config.networking.pppInterface;
in {
  options = {
    networking.pppInterface = mkOption {
      type = types.str;
      default = "dsl";
    };
  };
  
  config = {
    networking.vlans = {
      telekom = {
        id = 7;
        interface = "eno2";
      };
    };
    
    services.pppd = {
      enable = true;
      peers.telekom.config = ''
        nodefaultroute
        ifname ${pppInterface}
        lcp-echo-failure 1
        lcp-echo-interval 1
        maxfail 0
        mtu 1492
        mru 1492
        plugin rp-pppoe.so
        name telekom
        user 002576900250551137425220#0001@t-online.de
        telekom
        debug
      '';
    };
    systemd.services."pppd-telekom" = {
      stopIfChanged = true;
      
      serviceConfig = lib.mkForce {
        Type = "notify";
        PIDFile = "/run/pppd/${pppInterface}.pid";
        ExecStart = "${lib.getBin pkgs.ppp}/sbin/pppd call telekom up_sdnotify nolog +ipv6";
        Restart = "always";
        RestartSec = 5;

        RuntimeDirectory = "pppd";
        RuntimeDirectoryPreserve = true;
      };
    };
    sops.secrets."pap-secrets" = {
      format = "binary";
      sopsFile = ./pap-secrets;
      path = "/etc/ppp/pap-secrets";
    };

    environment.etc = {
      "ppp/ip-up" = {
        text = ''
          #!${pkgs.runtimeShell}
          ${pkgs.iproute}/bin/ip route add default via "$5" dev "${pppInterface}" metric 512
        '';
        mode = "0555";
      };
    };

    systemd.network.networks.${pppInterface} = {
      matchConfig = {
        Name = pppInterface;
      };
      dns = [ "::1" "127.0.0.1" ];
      domains = [ "~." ];
      networkConfig = {
        LinkLocalAddressing = "no";
        DNSSEC = true;
      };
    };

    services.corerad = {
      enable = true;
      settings = {
        interfaces = [
          { name = pppInterface;
            monitor = true;
            verbose = true;
          }
          { name = "lan";
            advertise = true;
            verbose = true;
            prefix = [{ prefix = "::/64"; }];
            route = [{ prefix = "::/0"; }];
            rdnss = [{ servers = ["::"]; }];
            dnssl = [{ domain_names = ["yggdrasil"]; }];
          }
        ];

        debug = {
          address = "localhost:9430";
          prometheus = true;
        };
      };
    };
    services.ndppd = {
      enable = true;
      proxies = {
        ${pppInterface} = {
          router = true;
          rules = {
            lan = {
              method = "iface";
              interface = "lan";
              network = "::/0";
            };
          };
        };
      };
    };
    boot.kernel.sysctl = {
      "net.ipv6.conf.all.forwarding" = true;
      "net.ipv6.conf.default.forwarding" = true;
      "net.ipv4.conf.all.forwarding" = true;
      "net.ipv4.conf.default.forwarding" = true;

      "net.core.rmem_max" = "4194304";
      "net.core.wmem_max" = "4194304";
    };
    systemd.services."pppd-telekom" = {
      bindsTo = [ "sys-subsystem-net-devices-telekom.device" ];
      after = [ "sys-subsystem-net-devices-telekom.device" ];
    };
    systemd.services."dhcpcd-${pppInterface}" = {
      wantedBy = [ "multi-user.target" "network-online.target" "pppd-telekom.service" ];
      bindsTo = [ "pppd-telekom.service" "sys-subsystem-net-devices-${pppInterface}.device" ];
      after = [ "pppd-telekom.service" "sys-subsystem-net-devices-${pppInterface}.device" ];
      wants = [ "network.target" ];
      before = [ "network-online.target" ];

      path = with pkgs; [ dhcpcd nettools openresolv ];
      unitConfig.ConditionCapability = "CAP_NET_ADMIN";

      stopIfChanged = true;

      preStart = ''
        i=0

        while [[ -z "$(${pkgs.iproute2}/bin/ip -6 addr show dev ${pppInterface} scope link)" ]]; do
          ${pkgs.coreutils}/bin/sleep 0.1
          i=$((i + 1))
          if [[ "$i" -ge 10 ]]; then
            exit 1
          fi
        done
      '';

      postStop = ''
        for dev in lan; do
          ${pkgs.iproute2}/bin/ip -6 a show dev "''${dev}" scope global | ${pkgs.gnugrep}/bin/grep inet6 | ${pkgs.gawk}/bin/awk '{ print $2; }' | ${pkgs.findutils}/bin/xargs -I '{}' -- ${pkgs.iproute2}/bin/ip addr del '{}' dev "''${dev}"
        done
      '';

      serviceConfig = let
        dhcpcdConf = pkgs.writeText "dhcpcd.conf" ''
          duid
          vendorclassid
          ipv6only

          nooption domain_name_servers, domain_name, domain_search
          option classless_static_routes
          option interface_mtu

          option host_name
          option rapid_commit
          require dhcp_server_identifier
          slaac private

          nohook resolv.conf
          ipv6ra_autoconf
          iaid 1195061668
          ipv6rs                 # enable routing solicitation for WAN adapter
          ia_pd 1 lan/0/64/0     # request a PD and assign it to the LAN

          reboot 0

          waitip 6
        '';
      in {
        Type = "forking";
        PIDFile = "/var/run/dhcpcd/${pppInterface}.pid";
        RuntimeDirectory = "dhcpcd";
        ExecStart = "@${pkgs.dhcpcd}/sbin/dhcpcd dhcpcd -q --config ${dhcpcdConf} ${pppInterface}";
        ExecReload = "${pkgs.dhcpcd}/sbin/dhcpcd --rebind ${pppInterface}";
        Restart = "always";
        RestartSec = "5";
      };
    };
    systemd.services.ndppd = {
      wantedBy = [ "dhcpcd-${pppInterface}.service" ];
      bindsTo = [ "dhcpcd-${pppInterface}.service" ];
      after = [ "dhcpcd-${pppInterface}.service" ];

      serviceConfig = {
        Restart = "always";
        RestartSec = "5";
      };
    };
    systemd.services.corerad = {
      wantedBy = [ "dhcpcd-${pppInterface}.service" ];
      bindsTo = [ "dhcpcd-${pppInterface}.service" ];
      after = [ "dhcpcd-${pppInterface}.service" ];

      serviceConfig = {
        Restart = lib.mkForce "always";
        RestartSec = "5";
      };
    };
    users.users.dhcpcd = {
      isSystemUser = true;
      group = "dhcpcd";
    };
    users.groups.dhcpcd = {};
  };
}