{ config, lib, pkgs, ... }:
with lib;
let
  cfg = config.services.ssh-tunnel;
in
{
  options.services.ssh-tunnel = {
    enable = mkEnableOption "SSH tunneling service";

    server = mkOption {
      type = with types; uniq str;
      default = null;
      description = "The SSH server to connect for port forwarding";
    };

    requiredBy = mkOption {
      type = with types; listOf str;
      default = [ ];
      description = "List of systemd services that require the SSH tunnels";
    };

    forwards = mkOption {
      type = types.submodule {
        options = {
          dynamic = mkOption {
            type = with types; listOf (either port str);
            default = [ ];
            description = "List of dynamic ports to open through the ssh tunnel. See ssh(1) for ``-D``";
          };
          local = mkOption {
            type = with types; listOf (either port str);
            default = [ ];
            description = "List of local ports to open through the ssh tunnel. See ssh(1) for ``-L``";
          };
          remote = mkOption {
            type = with types; listOf (either port str);
            default = [ ];
            description = "List of remote ports to open through the ssh tunnel. See ssh(1) for ``-R``";
          };
        };
      };
    };
  };

  config.systemd.services.ssh-tunnel = mkIf cfg.enable (
    let
      mkParams = flag: concatMapStringsSep " " (x: "${flag} ${toString x}");

      dynamic = mkParams "-D" cfg.forwards.dynamic;
      local = mkParams "-L" cfg.forwards.local;
      remote = mkParams "-R" cfg.forwards.remote;

      options = mkParams "-o" (mapAttrsToList (n: v: "${n}=${toString v}") {
        ServerAliveInterval = 60;
        ExitOnForwardFailure = "yes";
        KbdInteractiveAuthentication = "no";
      });
    in
    {
      script = ''
        ${pkgs.openssh}/bin/ssh ${cfg.server} -NTn ${options} ${dynamic} ${local} ${remote}
      '';
      requiredBy = cfg.requiredBy;
      serviceConfig = {
        RestartSec = 5;
        Restart = "always";
      };
    }
  );
}
