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

let
  inherit (lib)
    attrsToList
    getExe'
    imap0
    mapAttrsToList
    mkEnableOption
    mkIf
    mkOption
    optionalString
    ;

  inherit (lib.types)
    attrsOf
    bool
    enum
    package
    path
    str
    submodule
    ;

  settingsFormat = pkgs.formats.toml { };

  pykanidm = pkgs.python3.pkgs.callPackage ./packages/pykanidm.nix { };
  rlm_python = pkgs.callPackage ./packages/rlm_python.nix { inherit pykanidm; };

  cfg = config.services.k-radius;

  acmeDirectory = config.security.acme.certs.${cfg.domain}.directory;
in
{
  options.services.k-radius = {
    enable = mkEnableOption "a freeradius service linked to kanidm.";

    domain = mkOption {
      type = str;
      description = "The domain used for the RADIUS server.";
    };

    raddb = mkOption {
      type = path;
      default = "/var/lib/radius/raddb/";
      description = "The location of the raddb directory.";
    };

    settings = mkOption { inherit (settingsFormat) type; };

    freeradius = mkOption {
      type = package;
      default = pkgs.freeradius.overrideAttrs (old: {
        buildInputs = (old.buildInputs or [ ]) ++ [ (pkgs.python3.withPackages (ps: [ ps.kanidm ])) ];
      });
    };

    configDir = mkOption {
      type = path;
      default = "/var/lib/radius/raddb";
      description = "The path of the freeradius server configuration directory.";
    };

    authTokenFile = mkOption {
      type = path;
      description = "File to the auth token for the service account.";
    };

    extra-mods = mkOption {
      type = attrsOf path;
      default = { };
      description = "Additional files to be linked in mods-enabled.";
    };

    extra-sites = mkOption {
      type = attrsOf path;
      default = { };
      description = "Additional files to be linked in sites-enabled.";
    };

    dictionary = mkOption {
      type = attrsOf (enum [
        "abinary"
        "date"
        "ipaddr"
        "integer"
        "string"
      ]);
      default = { };
      description = "Declare additionnal attributes to be listed in the dictionary.";
    };

    radiusClients = mkOption {
      type = attrsOf (submodule {
        options = {
          secret = mkOption { type = path; };
          ipaddr = mkOption { type = str; };
        };
      });
      default = { };
      description = "A mapping of clients and their authentication tokens.";
    };

    checkConfiguration = mkOption {
      type = bool;
      description = "Check the configuration before starting the deamon. Useful for debugging.";
      default = false;
    };
  };

  config = mkIf cfg.enable {
    # Certificate setup
    services.nginx.virtualHosts.${cfg.domain} = {
      http2 = false;
      enableACME = true;
      forceSSL = true;
    };

    users = {
      users.radius = {
        group = "radius";
        description = "Radius daemon user";
        isSystemUser = true;
      };

      groups.radius = { };
    };

    systemd.services.radius = {
      description = "FreeRadius server";
      wantedBy = [ "multi-user.target" ];
      after = [
        "network.target"
        "acme-finished-${cfg.domain}.target"
      ];
      wants = [ "network.target" ];
      startLimitIntervalSec = 20;
      startLimitBurst = 5;

      preStart = ''
        raddb=${cfg.raddb}

        # Recreate the configuration directory
        rm -rf $raddb && mkdir -p $raddb

        cp -R --no-preserve=mode ${cfg.freeradius}/etc/raddb/* $raddb
        cp -R --no-preserve=mode ${rlm_python}/etc/raddb/* $raddb

        chmod -R u+w $raddb

        # disable auth via methods kanidm doesn't support
        rm $raddb/mods-available/sql
        rm $raddb/mods-enabled/{passwd,totp}

        # enable the python and cache modules
        ln -nsf $raddb/mods-available/python3 $raddb/mods-enabled/python3
        ln -nsf $raddb/sites-available/check-eap-tls $raddb/sites-enabled/check-eap-tls

        # write the clients configuration
        > $raddb/clients.conf
        ${builtins.concatStringsSep "\n" (
          builtins.attrValues (
            builtins.mapAttrs (
              name:
              { secret, ipaddr }:
              ''
                cat <<EOF >> $raddb/clients.conf
                client ${name} {
                    ipaddr = ${ipaddr}
                    secret = $(cat "${secret}")
                    proto  = *
                }
                EOF
              ''
            ) cfg.radiusClients
          )
        )}

        # Copy the kanidm configuration
        cat <<EOF > /var/lib/radius/kanidm.toml
        auth_token = "$(cat "${cfg.authTokenFile}")"
        EOF

        cat ${settingsFormat.generate "kanidm.toml" cfg.settings} >> /var/lib/radius/kanidm.toml
        chmod u+w /var/lib/radius/kanidm.toml

        # Copy the certificates to the correct directory
        rm -rf $raddb/certs && mkdir -p $raddb/certs

        cp ${acmeDirectory}/chain.pem $raddb/certs/ca.pem

        ${lib.getExe pkgs.openssl} rehash $raddb/certs

        # Recreate the dh.pem file
        ${lib.getExe pkgs.openssl} dhparam -in $raddb/certs/ca.pem -out $raddb/certs/dh.pem 2048

        cp ${acmeDirectory}/full.pem $raddb/certs/server.pem

        # Link the dictionary
        ln -nsf ${
          pkgs.writeText "radius-dictionary" (
            builtins.concatStringsSep "\n" (
              imap0 (i: { name, value }: "ATTRIBUTE ${name} ${builtins.toString (3000 + i)} ${value}") (
                attrsToList cfg.dictionary
              )
            )
          )
        } $raddb/dictionary

        # Link extra-mods
        ${builtins.concatStringsSep "\n" (
          mapAttrsToList (name: path: "ln -nsf ${path} $raddb/mods-enabled/${name}") cfg.extra-mods
        )}

        # Link extra-sites
        ${builtins.concatStringsSep "\n" (
          mapAttrsToList (name: path: "ln -nsf ${path} $raddb/sites-enabled/${name}") cfg.extra-sites
        )}

        # Check the configuration
        ${optionalString cfg.checkConfiguration "${getExe' pkgs.freeradius "radiusd"} -C -d $raddb -l stdout"}
      '';

      path = [
        pkgs.openssl
        pkgs.gnused
      ];

      environment = {
        KANIDM_RLM_CONFIG = "/var/lib/radius/kanidm.toml";
        PYTHONPATH = rlm_python.pythonPath;
      };

      serviceConfig = {
        ExecStart = "${cfg.freeradius}/bin/radiusd -X -f -d /var/lib/radius/raddb -l stdout";
        ExecReload = [
          "${cfg.freeradius}/bin/radiusd -C -d /var/lib/radius/raddb -l stdout"
          "${pkgs.coreutils}/bin/kill -HUP $MAINPID"
        ];
        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
        DynamicUser = true;
        Group = "radius";
        LogsDirectory = "radius";
        ReadOnlyPaths = [ acmeDirectory ];
        Restart = "on-failure";
        RestartSec = 2;
        RuntimeDirectory = "radius";
        StateDirectory = "radius";
        SupplementaryGroups = [ "nginx" ];
        User = "radius";
      };
    };
  };
}