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

with lib;

{
  options = {
    services.pptpd = {
      enable = mkEnableOption "pptpd, the Point-to-Point Tunneling Protocol daemon";

      serverIp = mkOption {
        type        = types.str;
        description = "The server-side IP address.";
        default     = "10.124.124.1";
      };

      clientIpRange = mkOption {
        type        = types.str;
        description = "The range from which client IPs are drawn.";
        default     = "10.124.124.2-11";
      };

      maxClients = mkOption {
        type        = types.int;
        description = "The maximum number of simultaneous connections.";
        default     = 10;
      };

      extraPptpdOptions = mkOption {
        type        = types.lines;
        description = "Adds extra lines to the pptpd configuration file.";
        default     = "";
      };

      extraPppdOptions = mkOption {
        type        = types.lines;
        description = "Adds extra lines to the pppd options file.";
        default     = "";
        example     = ''
          ms-dns 8.8.8.8
          ms-dns 8.8.4.4
        '';
      };
    };
  };

  config = mkIf config.services.pptpd.enable {
    systemd.services.pptpd = let
      cfg = config.services.pptpd;

      pptpd-conf = pkgs.writeText "pptpd.conf" ''
        # Inspired from pptpd-1.4.0/samples/pptpd.conf
        ppp ${ppp-pptpd-wrapped}/bin/pppd
        option ${pppd-options}
        pidfile /run/pptpd.pid
        localip ${cfg.serverIp}
        remoteip ${cfg.clientIpRange}
        connections ${toString cfg.maxClients} # (Will get harmless warning if inconsistent with IP range)

        # Extra
        ${cfg.extraPptpdOptions}
      '';

      pppd-options = pkgs.writeText "ppp-options-pptpd.conf" ''
        # From: cat pptpd-1.4.0/samples/options.pptpd | grep -v ^# | grep -v ^$
        name pptpd
        refuse-pap
        refuse-chap
        refuse-mschap
        require-mschap-v2
        require-mppe-128
        proxyarp
        lock
        nobsdcomp
        novj
        novjccomp
        nologfd

        # Extra:
        ${cfg.extraPppdOptions}
      '';

      ppp-pptpd-wrapped = pkgs.stdenv.mkDerivation {
        name         = "ppp-pptpd-wrapped";
        phases       = [ "installPhase" ];
        nativeBuildInputs  = with pkgs; [ makeWrapper ];
        installPhase = ''
          mkdir -p $out/bin
          makeWrapper ${pkgs.ppp}/bin/pppd $out/bin/pppd \
            --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
            --set NIX_REDIRECTS "/etc/ppp=/etc/ppp-pptpd"
        '';
      };
    in {
      description = "pptpd server";

      requires = [ "network-online.target" ];
      wantedBy = [ "multi-user.target" ];

      preStart = ''
        mkdir -p -m 700 /etc/ppp-pptpd

        secrets="/etc/ppp-pptpd/chap-secrets"

        [ -f "$secrets" ] || cat > "$secrets" << EOF
        # From: pptpd-1.4.0/samples/chap-secrets
        # Secrets for authentication using CHAP
        # client	server	secret		IP addresses
        #username	pptpd	password	*
        EOF

        chown root:root "$secrets"
        chmod 600 "$secrets"
      '';

      serviceConfig = {
        ExecStart = "${pkgs.pptpd}/bin/pptpd --conf ${pptpd-conf}";
        KillMode  = "process";
        Restart   = "on-success";
        Type      = "forking";
        PIDFile   = "/run/pptpd.pid";
      };
    };
  };
}
