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

  cfg = config.services.froide-govplan;
  pythonFmt = pkgs.formats.pythonVars { };
  settingsFile = pythonFmt.generate "extra_settings.py" cfg.settings;

  pkg = cfg.package.overridePythonAttrs (old: {
    src = ./.;
    dependencies = old.dependencies ++ [ cfg.package.python.pkgs.mastodon-py ];
    postInstall =
      old.postInstall
      + ''
        ln -s ${settingsFile} $out/${pkg.python.sitePackages}/froide_govplan/project/extra_settings.py

        rm -r $out/${pkgs.python3.sitePackages}/froide_govplan/templates
        ln -sf /var/lib/froide-govplan/templates $out/${pkgs.python3.sitePackages}/froide_govplan/templates
        cp -r froide_govplan/static $out/${pkgs.python3.sitePackages}/froide_govplan/
      '';
  });

  froide-govplan = pkgs.writeShellApplication {
    name = "froide-govplan";
    runtimeInputs = [ pkgs.coreutils ];
    text = ''
      SUDO="exec"
      if [[ "$USER" != govplan ]]; then
        SUDO="exec /run/wrappers/bin/sudo -u govplan"
      fi
      $SUDO env ${lib.getExe pkg} "$@"
    '';
  };

  # Service hardening
  defaultServiceConfig = {
    # Secure the services
    ReadWritePaths = [ cfg.dataDir ];
    CacheDirectory = "froide-govplan";
    CapabilityBoundingSet = "";
    # ProtectClock adds DeviceAllow=char-rtc r
    DeviceAllow = "";
    LockPersonality = true;
    MemoryDenyWriteExecute = true;
    NoNewPrivileges = true;
    PrivateDevices = true;
    PrivateMounts = true;
    PrivateTmp = true;
    PrivateUsers = true;
    ProtectClock = true;
    ProtectHome = true;
    ProtectHostname = true;
    ProtectSystem = "strict";
    ProtectControlGroups = true;
    ProtectKernelLogs = true;
    ProtectKernelModules = true;
    ProtectKernelTunables = true;
    ProtectProc = "invisible";
    ProcSubset = "pid";
    RestrictAddressFamilies = [
      "AF_UNIX"
      "AF_INET"
      "AF_INET6"
    ];
    RestrictNamespaces = true;
    RestrictRealtime = true;
    RestrictSUIDSGID = true;
    SystemCallArchitectures = "native";
    SystemCallFilter = [
      "@system-service"
      "~@privileged @setuid @keyring"
    ];
    UMask = "0066";
  };

in
{
  options.services.froide-govplan = {

    enable = lib.mkEnableOption "Gouvernment planer web app Govplan";

    package = lib.mkPackageOption pkgs "froide-govplan" { };

    hostName = lib.mkOption {
      type = lib.types.str;
      default = "localhost";
      description = "FQDN for the froide-govplan instance.";
    };

    dataDir = lib.mkOption {
      type = lib.types.str;
      default = "/var/lib/froide-govplan";
      description = "Directory to store the Froide-Govplan server data.";
    };

    secretKeyFile = lib.mkOption {
      type = lib.types.nullOr lib.types.path;
      default = null;
      description = ''
        Path to a file containing the secret key.
      '';
    };

    settings = lib.mkOption {
      description = ''
        Configuration options to set in `extra_settings.py`.
      '';

      default = { };

      type = lib.types.submodule {
        freeformType = pythonFmt.type;

        options = {
          ALLOWED_HOSTS = lib.mkOption {
            type = with lib.types; listOf str;
            default = [ "*" ];
            description = ''
              A list of valid fully-qualified domain names (FQDNs) and/or IP
              addresses that can be used to reach the Froide-Govplan service.
            '';
          };
        };
      };
    };

  };

  config = lib.mkIf cfg.enable {

    services.froide-govplan = {
      settings = {
        STATIC_ROOT = "${cfg.dataDir}/static";
        DEBUG = false;
        DATABASES.default = {
          ENGINE = "django.contrib.gis.db.backends.postgis";
          NAME = "govplan";
          USER = "govplan";
          HOST = "/run/postgresql";
        };
      };
    };

    services.postgresql = {
      enable = true;
      ensureDatabases = [ "govplan" ];
      ensureUsers = [
        {
          name = "govplan";
          ensureDBOwnership = true;
        }
      ];
      extensions = ps: with ps; [ postgis ];
    };

    services.nginx = {
      enable = lib.mkDefault true;
      virtualHosts."${cfg.hostName}".locations = {
        "/".extraConfig = "proxy_pass http://unix:/run/froide-govplan/froide-govplan.socket;";
        "/static/".alias = "${cfg.dataDir}/static/";
      };
      proxyTimeout = lib.mkDefault "120s";
    };

    systemd = {
      services = {

        postgresql.serviceConfig.ExecStartPost =
          let
            sqlFile = pkgs.writeText "immich-pgvectors-setup.sql" ''
              CREATE EXTENSION IF NOT EXISTS postgis;
            '';
          in
          [
            ''
              ${lib.getExe' config.services.postgresql.package "psql"} -d govplan -f "${sqlFile}"
            ''
          ];

        froide-govplan = {
          description = "Gouvernment planer Govplan";
          serviceConfig = defaultServiceConfig // {
            WorkingDirectory = cfg.dataDir;
            StateDirectory = lib.mkIf (cfg.dataDir == "/var/lib/froide-govplan") "froide-govplan";
            User = "govplan";
            Group = "govplan";
          };
          after = [
            "postgresql.service"
            "network.target"
            "systemd-tmpfiles-setup.service"
          ];
          wantedBy = [ "multi-user.target" ];
          environment =
            {
              PYTHONPATH = pkg.python.pkgs.makePythonPath (with pkg.python.pkgs; [

    bleach
    django-admin-sortable2
    django-cms
    django-filer
    django-mfa3
    django-mptt
    django-oauth-toolkit
    django-sekizai
    django-tinymce
    django-treebeard
    djangocms-alias
    # Downgrade to last working version
    (toPythonModule (
      pkgs.froide.overridePythonAttrs (prev: {
        nativeBuildInputs = [ pkgs.makeBinaryWrapper ];
        postBuild = "";
        doCheck = false;
        pnpmDeps = null;
        src = prev.src.override {
          rev = "a78a4054f9f37b0a5109a6d8cfbbda742f86a8ca";
          hash = "sha256-gtOssbsVf3nG+pmLPgvh4685vHh2x+jlXiTjU+JhQa8=";
        };
      })
    ))
    psycopg
	      mastodon-py
	      (toPythonModule (pkg))
	      ]);
              GDAL_LIBRARY_PATH = "${pkgs.gdal}/lib/libgdal.so";
              GEOS_LIBRARY_PATH = "${pkgs.geos}/lib/libgeos_c.so";
            }
            // lib.optionalAttrs (cfg.secretKeyFile != null) {
              SECRET_KEY_FILE = cfg.secretKeyFile;
            };
          preStart = ''
            # Auto-migrate on first run or if the package has changed
            versionFile="${cfg.dataDir}/src-version"
            version=$(cat "$versionFile" 2>/dev/null || echo 0)

            if [[ $version != ${pkg.version} ]]; then
              ${lib.getExe pkg} migrate --no-input
              ${lib.getExe pkg} collectstatic --no-input --clear
              echo ${pkg.version} > "$versionFile"
            fi
          '';
          script = ''
            ${pkg.python.pkgs.uvicorn}/bin/uvicorn --uds /run/froide-govplan/froide-govplan.socket \
              --app-dir ${pkg}/${pkg.python.sitePackages}/froide_govplan \
              project.asgi:application
          '';
        };
      };

    };

    systemd.tmpfiles.rules = [ "d /run/froide-govplan - govplan govplan - -" ];

    environment.systemPackages = [ froide-govplan ];

    users.users.govplan = {
      home = "${cfg.dataDir}";
      isSystemUser = true;
      group = "govplan";
    };
    users.groups.govplan = { };

  };

  meta.maintainers = with lib.maintainers; [ onny ];

}
