{ config, pkgs, lib, ... }:
let
  cfg = config.bordervm;
  inherit (lib) mkOption mkEnableOption mdDoc types optional optionals;
in {
  options.bordervm = {
    keys = mkOption {
      type = types.listOf types.str;
      default = [];
    };
    l2tp = {
      host = mkOption {
        description = mdDoc ''
          Hostname or IP address of an L2TP LNS that this VM
          will connect to when it receives a PPPoE connection request
        '';
        type = types.str;
        example = "l2tp.example.org";
      };
      port = mkOption {
        description = mdDoc ''
          Port number, if non-standard, of the LNS.
        '';
        type = types.int;
        default = 1701;
      };
    };
    ethernet = {
      pci = {
        enable = mkEnableOption "passthru PCI ethernet";
        id = mkOption {
          description = ''
            Host PCI ID (as shown by `lspci`) of the ethernet adaptor
            to be used by the VM. This uses VFIO and requires setup
            on the emulation host before it will work!
          '';
          type = types.str;
          example = "04:00.0";
        };
      };
      usb = {
        enable = mkEnableOption "passthru USB ethernet";
        vendor = mkOption {
          type = types.str;
          example = "0x0bda";
        };
        product = mkOption {
          type = types.str;
          example = "0x8153";
        };
      };
    };
  };
  imports = [
    <nixpkgs/nixos/modules/virtualisation/qemu-vm.nix>
  ];
  config = {
    boot.kernelParams = [
      "loglevel=9"
    ];
    systemd.services.pppoe =
      let conf = pkgs.writeText "kpppoed.toml"
        ''
      interface_name = "eth1"
      services = [ "myservice" ]
      lns_ipaddr = "${cfg.l2tp.host}:${builtins.toString cfg.l2tp.port}"
      ac_name = "kpppoed-1.0"
    '';
      in  {
        wantedBy = [ "multi-user.target" ];
        after = [ "network-online.target" ];
        serviceConfig = {
          ExecStart = "${pkgs.go-l2tp}/bin/kpppoed -config ${conf}";
        };
      };
    systemd.services.tufted = {
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        ExecStart = "${pkgs.tufted}/bin/tufted /home/liminix/liminix";
      };
    };
    services.openssh.enable = true;
    services.dnsmasq = {
      enable = true;
      resolveLocalQueries = false;
      settings =  {
        # domain-needed = true;
        dhcp-range = [ "10.0.0.10,10.0.0.240" ];
        interface = "eth1";
      };
    };

    systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ];


    virtualisation = {
      qemu = {
        networkingOptions = [];
        options = [] ++
          optional cfg.ethernet.pci.enable
            "-device vfio-pci,host=${cfg.ethernet.pci.id}" ++
          optionals cfg.ethernet.usb.enable [
            "-device usb-ehci,id=ehci"
            "-device usb-host,bus=ehci.0,vendorid=${cfg.ethernet.usb.vendor},productid=${cfg.ethernet.usb.product}"
          ] ++ [
            "-nographic"
            "-serial mon:stdio"
          ];
      };
      sharedDirectories = {
        liminix = {
          source = builtins.toString ./.;
          target = "/home/liminix/liminix";
        };
      };
    };
    environment.systemPackages =
      let wireshark-nogui = pkgs.wireshark.override { withQt = false ; };
          in with pkgs; [
            tcpdump
            wireshark-nogui
            socat
            tufted
            iptables
            usbutils
            busybox
          ];
    security.sudo.wheelNeedsPassword = false;
    networking = {
      hostName = "border";
      firewall = { enable = false; };
      interfaces.eth1 = {
        useDHCP = false;
        ipv4.addresses = [ { address = "10.0.0.1"; prefixLength = 24;}];
      };
      nat = {
        enable = true;
        internalInterfaces = [ "eth1" ];
        externalInterface ="eth0";
      };
    };
    users.users.liminix = {
      isNormalUser = true;
      uid = 1000;
      extraGroups = [ "wheel"];
      openssh.authorizedKeys.keys = cfg.keys;
    };
    services.getty.autologinUser = "liminix";
  };
}