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

let

  inherit (builtins) toFile;
  inherit (lib)
    concatMapStrings
    concatStringsSep
    mapAttrsToList
    mkIf
    mkEnableOption
    mkOption
    types
    literalExpression
    optionalString
    ;

  cfg = config.services.strongswan;

  ipsecSecrets = secrets: concatMapStrings (f: "include ${f}\n") secrets;

  ipsecConf =
    {
      setup,
      connections,
      ca,
    }:
    let
      # https://wiki.strongswan.org/projects/strongswan/wiki/IpsecConf
      makeSections =
        type: sections:
        concatStringsSep "\n\n" (
          mapAttrsToList (
            sec: attrs:
            "${type} ${sec}\n" + (concatStringsSep "\n" (mapAttrsToList (k: v: "  ${k}=${v}") attrs))
          ) sections
        );
      setupConf = makeSections "config" { inherit setup; };
      connectionsConf = makeSections "conn" connections;
      caConf = makeSections "ca" ca;

    in
    builtins.toFile "ipsec.conf" ''
      ${setupConf}
      ${connectionsConf}
      ${caConf}
    '';

  strongswanConf =
    {
      setup,
      connections,
      ca,
      secretsFile,
      managePlugins,
      enabledPlugins,
    }:
    toFile "strongswan.conf" ''
      charon {
        ${optionalString managePlugins "load_modular = no"}
        ${optionalString managePlugins ("load = " + (concatStringsSep " " enabledPlugins))}
        plugins {
          stroke {
            secrets_file = ${secretsFile}
          }
        }
      }

      starter {
        config_file = ${ipsecConf { inherit setup connections ca; }}
      }
    '';

in
{
  options.services.strongswan = {
    enable = mkEnableOption "strongSwan";

    secrets = mkOption {
      type = types.listOf types.str;
      default = [ ];
      example = [ "/run/keys/ipsec-foo.secret" ];
      description = ''
        A list of paths to IPSec secret files. These
        files will be included into the main ipsec.secrets file with
        the `include` directive. It is safer if these
        paths are absolute.
      '';
    };

    setup = mkOption {
      type = types.attrsOf types.str;
      default = { };
      example = {
        cachecrls = "yes";
        strictcrlpolicy = "yes";
      };
      description = ''
        A set of options for the ‘config setup’ section of the
        {file}`ipsec.conf` file. Defines general
        configuration parameters.
      '';
    };

    connections = mkOption {
      type = types.attrsOf (types.attrsOf types.str);
      default = { };
      example = literalExpression ''
        {
          "%default" = {
            keyexchange = "ikev2";
            keyingtries = "1";
          };
          roadwarrior = {
            auto       = "add";
            leftcert   = "/run/keys/moonCert.pem";
            leftid     = "@moon.strongswan.org";
            leftsubnet = "10.1.0.0/16";
            right      = "%any";
          };
        }
      '';
      description = ''
        A set of connections and their options for the ‘conn xxx’
        sections of the {file}`ipsec.conf` file.
      '';
    };

    ca = mkOption {
      type = types.attrsOf (types.attrsOf types.str);
      default = { };
      example = {
        strongswan = {
          auto = "add";
          cacert = "/run/keys/strongswanCert.pem";
          crluri = "http://crl2.strongswan.org/strongswan.crl";
        };
      };
      description = ''
        A set of CAs (certification authorities) and their options for
        the ‘ca xxx’ sections of the {file}`ipsec.conf`
        file.
      '';
    };

    managePlugins = mkOption {
      type = types.bool;
      default = false;
      description = ''
        If set to true, this option will disable automatic plugin loading and
        then tell strongSwan to enable the plugins specified in the
        {option}`enabledPlugins` option.
      '';
    };

    enabledPlugins = mkOption {
      type = types.listOf types.str;
      default = [ ];
      description = ''
        A list of additional plugins to enable if
        {option}`managePlugins` is true.
      '';
    };
  };

  config =
    with cfg;
    mkIf enable {

      # here we should use the default strongswan ipsec.secrets and
      # append to it (default one is empty so not a pb for now)
      environment.etc."ipsec.secrets".text = ipsecSecrets cfg.secrets;

      systemd.services.strongswan = {
        description = "strongSwan IPSec Service";
        wantedBy = [ "multi-user.target" ];
        path = with pkgs; [
          kmod
          iproute2
          iptables
          util-linux
        ]; # XXX Linux
        wants = [ "network-online.target" ];
        after = [ "network-online.target" ];
        environment = {
          STRONGSWAN_CONF = strongswanConf {
            inherit
              setup
              connections
              ca
              managePlugins
              enabledPlugins
              ;
            secretsFile = "/etc/ipsec.secrets";
          };
        };
        serviceConfig = {
          ExecStart = "${pkgs.strongswan}/sbin/ipsec start --nofork";
        };
        preStart = ''
          # with 'nopeerdns' setting, ppp writes into this folder
          mkdir -m 700 -p /etc/ppp
        '';
      };
    };
}
