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

with lib;

let
  pppInterface = config.networking.pppInterface;
in {
  options = {
    networking.pppInterface = mkOption {
      type = types.str;
      default = "gpon";
    };
  };

  config = {
    networking.vlans = {
      telekom = {
        id = 7;
        interface = "eno2";
      };
    };

    services.pppd = {
      enable = true;
      peers.telekom.config = ''
        nodefaultroute
        ifname ${pppInterface}
        lcp-echo-adaptive
        lcp-echo-failure 5
        lcp-echo-interval 1
        maxfail 0
        mtu 1492
        mru 1492
        plugin pppoe.so
        name telekom
        user 002576900250551137425220#0001@t-online.de
        nic-telekom
        debug
        +ipv6
      '';
    };
    systemd.services."pppd-telekom" = {
      stopIfChanged = true;

      serviceConfig = {
        PIDFile = "/run/pppd/${pppInterface}.pid";
      };
      restartTriggers = with config; [
        environment.etc."ppp/ip-pre-up".source
        environment.etc."ppp/ip-up".source
        environment.etc."ppp/ip-down".source
        # sops.secrets."pap-secrets".sopsFile
      ];
    };
    sops.secrets."pap-secrets" = {
      format = "binary";
      sopsFile = ./pap-secrets;
      path = "/etc/ppp/pap-secrets";
    };

    environment.etc = {
      "ppp/ip-pre-up".source = let
        app = pkgs.writeShellApplication {
          name = "ip-pre-up";
          runtimeInputs = with pkgs; [ iproute2 ethtool ];
          text = ''
            ethtool -K telekom tso off gso off gro off

            ip link del "ifb4${pppInterface}" || true
            ip link add name "ifb4${pppInterface}" type ifb
            ip link set "ifb4${pppInterface}" up

            tc qdisc del dev "ifb4${pppInterface}" root || true
            tc qdisc del dev "${pppInterface}" ingress || true
            tc qdisc del dev "${pppInterface}" root || true

            tc qdisc add dev "${pppInterface}" handle ffff: ingress
            tc filter add dev "${pppInterface}" parent ffff: basic action ctinfo dscp 0x0000003f 0x00000040 action mirred egress redirect dev "ifb4${pppInterface}"
            tc qdisc replace dev "ifb4${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional diffserv4 bandwidth 285mbit
            tc qdisc replace dev "${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional nat diffserv4 wash bandwidth 143mbit
          '';
        };
      in "${app}/bin/${app.meta.mainProgram}";
      "ppp/ip-up".source = let
        app = pkgs.writeShellApplication {
          name = "ip-up";
          runtimeInputs = with pkgs; [ iproute2 ];
          text = ''
            ip route add default via "$5" dev "${pppInterface}" metric 512
          '';
        };
      in "${app}/bin/${app.meta.mainProgram}";
      "ppp/ip-down".source = let
        app = pkgs.writeShellApplication {
          name = "ip-down";
          runtimeInputs = with pkgs; [ iproute2 ];
          text = ''
            ip link del "ifb4${pppInterface}"
          '';
        };
      in "${app}/bin/${app.meta.mainProgram}";
    };

    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"]; }];
            # other_config = true;
          }
        ];

        debug = {
          address = "localhost:9430";
          prometheus = true;
        };
      };
    };
    services.ndppd = {
      enable = true;
      proxies = {
        ${pppInterface} = {
          router = true;
          rules = {
            lan = {
              method = "iface";
              interface = "lan";
              network = "::/0";
            };
          };
        };
      };
    };
    boot.kernelModules = [ "ifb" ];
    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" ];
      after = [ "pppd-telekom.service" ];
      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 = {};

    systemd.services.unbound = {
      wantedBy = [ "dhcpcd-${pppInterface}.service" ];
      bindsTo = [ "dhcpcd-${pppInterface}.service" ];
      after = [ "dhcpcd-${pppInterface}.service" ];

      serviceConfig = {
        Restart = lib.mkForce "always";
      };
    };
  };
}