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

let
  inherit (lib)
    mkDefault
    mkEnableOption
    mkIf
    mkOption
    types
    ;

  yamlFormat = pkgs.formats.yaml { };

  configFile = yamlFormat.generate "proxy_conf.yaml" cfg.proxyConf;

  cfg = config.services.satosa;

  mkYamlFiles =
    files: builtins.attrValues (builtins.mapAttrs (name: yamlFormat.generate "${name}.yaml") files);

  pyEnv = cfg.package.python.withPackages (ps: [
    cfg.package
    ps.gunicorn
  ]);
in
{
  options.services.satosa = {
    enable = mkEnableOption "SATOSA, a SAML and OIDC proxy.";

    package = mkOption {
      type = types.package;
      default = import ./package { inherit pkgs; };
    };

    port = mkOption {
      type = types.port;
      default = 8080;
    };

    host = mkOption { type = types.str; };

    workers = mkOption {
      type = types.int;
      default = 1;
    };

    configureNginx = mkOption {
      type = types.bool;
      default = true;
    };

    proxyConf = mkOption {
      inherit (yamlFormat) type;
      default = { };
    };

    envFile = mkOption {
      type = with types; nullOr path;
      default = null;
    };

    internalAttributes = mkOption {
      inherit (yamlFormat) type;
      default = { };
    };

    frontendModules = mkOption {
      type = types.attrsOf yamlFormat.type;
      default = { };
    };

    backendModules = mkOption {
      type = types.attrsOf yamlFormat.type;
      default = { };
    };

    microServices = mkOption {
      type = types.attrsOf yamlFormat.type;
      default = { };
    };
  };

  config = mkIf cfg.enable {
    services.satosa.proxyConf = builtins.mapAttrs (_: mkDefault) {
      BASE = "https://${cfg.host}";
      COOKIE_STATE_NAME = "satosa_state";
      COOKIE_SECURE = true;
      COOKIE_HTTPONLY = true;
      COOKIE_SAMESITE = "None";
      COOKIE_MAX_AGE = "1200";
      CONTEXT_STATE_DELETE = true;
      INTERNAL_ATTRIBUTES = yamlFormat.generate "internal_attributes.yaml" {
        attributes = cfg.internalAttributes;
      };
      BACKEND_MODULES = mkYamlFiles cfg.backendModules;
      FRONTEND_MODULES = mkYamlFiles cfg.frontendModules;
      MICRO_SERVICES = mkYamlFiles cfg.microServices;
      LOGGING = {
        version = 1;
        formatters.simple.format = "[%(asctime)s] [%(levelname)s] [%(name)s.%(funcName)s] %(message)s";
        handlers.stdout = {
          class = "logging.StreamHandler";
          stream = "ext://sys.stdout";
          level = "DEBUG";
          formatter = "simple";
        };
        loggers = {
          satosa.level = "DEBUG";
          saml2.level = "DEBUG";
          oidcendpoint.level = "DEBUG";
          pyop.level = "DEBUG";
          oic.level = "DEBUG";
          root = {
            level = "DEBUG";
            handlers = [ "stdout" ];
          };
        };
      };
    };

    systemd.services = {
      satosa-metadata = {
        script = ''
          umask 077

          # Generate a secret key/certificate if none are present
          mkdir -p ssl
          if [ ! -f "ssl/.created" ]; then
            ${pkgs.openssl}/bin/openssl req -x509 \
            -newkey rsa:2048 \
            -keyout ssl/key.pem \
            -out ssl/cert.pem \
            -sha256 \
            -days 3650 \
            -nodes \
            -subj "/C=FR/ST=Île de France/L=Paris/O=DGNum/OU=./CN=saml-idp.dgnum.eu" \
            && touch ssl/.created
          fi

          mkdir -p metadata

          ${cfg.package}/bin/satosa-saml-metadata \
          --dir metadata \
          --sign ${configFile} ssl/key.pem ssl/cert.pem
        '';

        serviceConfig = {
          Type = "oneshot";
          User = "satosa";
          Group = "satosa";
          DynamicUser = true;
          StateDirectory = "satosa";
          WorkingDirectory = "/var/lib/satosa";
          EnvironmentFile = lib.optional (cfg.envFile != null) cfg.envFile;
        };
      };

      satosa = {
        wantedBy = [ "multi-user.target" ];
        after = [ "network.target" ];
        wants = [ "satosa-metadata.service" ];
        serviceConfig = {
          User = "satosa";
          Group = "satosa";
          DynamicUser = true;
          Type = "notify";
          RuntimeDirectory = "satosa";
          StateDirectory = "satosa";
          WorkingDirectory = cfg.package;
          ExecStart = ''
            ${pyEnv}/bin/gunicorn \
            -w ${builtins.toString cfg.workers} \
            -b 127.0.0.1:${builtins.toString cfg.port} \
            --pythonpath ${pyEnv}/${pkgs.python3.sitePackages} \
            satosa.wsgi:app
          '';
          ExecReload = "${pkgs.util-linux}/bin/kill -s HUP $MAINPID";
          KillMode = "mixed";
          TimeoutStopSec = "5";
          EnvironmentFile = lib.optional (cfg.envFile != null) cfg.envFile;
        };
        environment = {
          SATOSA_CONFIG = configFile;
        };
      };
    };

    services.nginx = mkIf cfg.configureNginx {
      enable = true;

      virtualHosts.${cfg.host} = {
        locations."/".proxyPass = "http://127.0.0.1:${builtins.toString cfg.port}";
      };
    };

    users.users.satosa = {
      isSystemUser = true;
      group = "satosa";
      home = "/var/lib/satosa";
    };
    users.groups.satosa = { };
  };
}