{
  lib,
  config,
  pkgs,
  ...
}:
let
  inherit (lib)
    mkEnableOption
    mkOption
    types
    mkIf
    ;

  configFormat = pkgs.formats.json { };
  cfg = config.services.inventree;
  pkg = cfg.package;
  configFile = "${cfg.dataDir}/config.json";

  env = {
    INVENTREE_CONFIG_FILE = configFile;
    INVENTREE_SECRET_KEY_FILE = cfg.secretKeyFile;
    INVENTREE_AUTO_UPDATE = "1";
    INVENTREE_PLUGINS_ENABLED = "1";
    INVENTREE_PLUGIN_NOINSTALL = "0";
    INVENTREE_STATIC_ROOT = cfg.config.static_root;
    INVENTREE_MEDIA_ROOT = cfg.config.media_root;
    INVENTREE_BACKUP_DIR = cfg.config.backup_dir;
    INVENTREE_OIDC_PRIVATE_KEY_FILE = cfg.config.oidc_private_key_file;
    INVENTREE_DB_ENGINE = cfg.config.database.ENGINE;
    INVENTREE_DB_NAME = cfg.config.database.NAME;
    INVENTREE_DB_HOST = cfg.config.database.HOST;
    INVENTREE_DB_USER = "inventree";
    INVENTREE_ADMIN_USER = cfg.config.adminUser;
    INVENTREE_ADMIN_PASSWORD_FILE = cfg.config.adminPasswordFile;
    INVENTREE_USE_X_FORWARDED_HOST = "1";
    INVENTREE_CORS_ORIGIN_ALLOW_ALL = "1";
    INVENTREE_FRONTEND_SETTINGS = ''{"mobile_mode":"allow-always"}'';

    INVENTREE_SITE_URL = cfg.config.site_url;

    PYTHONPATH = pkg.pythonPath;
  };

  inventree-invoke = pkgs.writeShellApplication {
    name = "inventree-invoke";
    text = ''
      export INVENTREE_CONFIG_FILE=${configFile}
      export INVENTREE_SECRET_KEY_FILE=${cfg.secretKeyFile}
      export PYTHONPATH=${pkg.pythonPath}

      exec -a "$0" ${pkgs.python3Packages.invoke}/bin/invoke -r ${cfg.package}/opt/inventree "$@"
    '';
  };
in
{
  options.services.inventree = {
    enable = mkEnableOption "InvenTree parts manager";

    package = lib.mkOption {
      type = types.package;
      default = pkgs.inventree;
      description = ''
        InvenTree package to use
      '';
    };

    hostName = mkOption {
      type = types.str;
      description = "FQDN for the InvenTree instance.";
    };

    dataDir = mkOption {
      type = types.path;
      default = "/var/lib/inventree";
      example = "/var/lib/inventree";
      description = ''
        The default path for all inventree data.
      '';
    };

    secretKeyFile = mkOption {
      type = types.path;
      default = "${cfg.dataDir}/secret_key.txt";
      description = ''
        Path to a file containing the secret key
      '';
    };

    config = mkOption {
      type = types.submodule ({
        freeformType = configFormat.type;
        options = {
          adminUser = mkOption {
            type = types.str;
            default = "admin";
          };
          adminPasswordFile = mkOption {
            type = types.path;
            description = "Path to password file for user `admin`";
          };
          site_url = mkOption {
            type = types.str;
            default = "https://${cfg.hostName}";
          };
          static_root = mkOption {
            type = types.path;
            default = "${cfg.dataDir}/static";
            description = ''
              Static file storage
            '';
          };
          media_root = mkOption {
            type = types.path;
            default = "${cfg.dataDir}/media_root";
            description = "Media root directory";
          };
          backup_dir = mkOption {
            type = types.path;
            default = "${cfg.dataDir}/backups";
            description = "Backup directory";
          };
          oidc_private_key_file = mkOption {
            type = types.path;
            default = "${cfg.dataDir}/oidc.key";
          };
        };
      });
      default = { };
      description = ''
        Config options, see https://docs.inventree.org/en/stable/start/config/
        for details
      '';
    };

    serverStartTimeout = mkOption {
      type = types.str;
      default = "10min";
      description = ''
        TimeoutStartSec for the server systemd service.
        See https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#TimeoutStartSec=
        for more details
      '';
    };

    serverStopTimeout = mkOption {
      type = types.str;
      default = "5min";
      description = ''
        TimeoutStopSec for the server systemd service.
        See https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#TimeoutStopSec=
        for more details
      '';
    };

  };

  config = mkIf cfg.enable {
    environment.systemPackages = [ inventree-invoke ];

    systemd.tmpfiles.rules = (
      map (dir: "d ${dir} 0755 inventree inventree") [
        "${cfg.dataDir}"
        "${cfg.dataDir}/static"
        "${cfg.dataDir}/media_root"
        "${cfg.dataDir}/backups"
      ]
    );

    services.inventree.config = {
      plugins_enabled = false;
      plugin_file = "${cfg.dataDir}/plugins.txt";
      plugin_dir = "${cfg.dataDir}/plugins";
      database = {
        ENGINE = "postgresql";
        NAME = "inventree";
        HOST = "/run/postgresql";
      };
    };

    services.postgresql = {
      enable = true;
      ensureDatabases = [ "inventree" ];
      ensureUsers = [
        {
          name = "inventree";
          ensureDBOwnership = true;
        }
      ];
    };

    users.users.inventree = {
      group = "inventree";
      isSystemUser = true;
      description = "InvenTree daemon user";
    };

    users.groups.inventree = { };

    services.nginx.enable = true;

    services.nginx.virtualHosts.${cfg.hostName} = {
      locations =
        let
          unixPath = config.systemd.sockets.inventree-gunicorn.socketConfig.ListenStream;
        in
        {
          "/" = {
            extraConfig = ''
              client_max_body_size 100M;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            '';
            proxyPass = "http://unix:${unixPath}";
          };
          "/static/" = {
            alias = "${cfg.config.static_root}/";
            extraConfig = ''
              expires 30d;
            '';
          };
          "/media/" = {
            alias = "${cfg.config.media_root}/";
            extraConfig = ''
              auth_request /auth;
            '';
          };
          "/auth" = {
            extraConfig = ''
              internal;
            '';
            proxyPass = "http://unix:${unixPath}:/auth/";
          };
        };
    };

    systemd.targets.inventree = {
      description = "Target for all InvenTree services";
      wantedBy = [ "multi-user.target" ];
      wants = [ "network-online.target" ];
      after = [ "network-online.target" ];
    };

    systemd.services.inventree-config = {
      description = "Inventree config generation";
      wantedBy = [ "inventree.target" ];
      partOf = [ "inventree.target" ];
      before = [
        "inventree-static.service"
        "inventree-gunicorn.service"
        "inventree-qcluster.service"
      ];
      serviceConfig = {
        # User = "root";
        # Group = "root";
        User = "inventree";
        Group = "inventree";
        Type = "oneshot";
        RemainAfterExit = true;
        PrivateTmp = true;
      };
      environment = env;
      script = ''
        set -euo pipefail

        umask u=rwx,g=,o=

        # chown inventree:inventree ${configFile}

        ${pkg}/opt/inventree/src/backend/InvenTree/manage.py migrate
      '';
    };

    systemd.services.inventree-static = {
      description = "InvenTree static migration";
      wantedBy = [ "inventree.target" ];
      partOf = [ "inventree.target" ];
      before = [ "inventree-gunicorn.service" ];
      environment = env;
      serviceConfig = {
        User = "inventree";
        Group = "inventree";
        StateDirectory = "inventree";
        #RuntimeDirectory = "inventree";
        PrivateTmp = true;
        ExecStart = ''
          ${pkg}/opt/inventree/src/backend/InvenTree/manage.py collectstatic  --no-input
        '';
      };
    };

    systemd.services.inventree-gunicorn = {
      description = "InvenTree Gunicorn server";
      requiredBy = [ "inventree.target" ];
      partOf = [ "inventree.target" ];
      #wantedBy = [ "inventree.target" ];
      environment = env;
      serviceConfig = {
        User = "inventree";
        Group = "inventree";
        StateDirectory = "inventree";
        #RuntimeDirectory = "inventree";
        PrivateTmp = true;
        ExecStart = ''
          ${pkg.gunicorn}/bin/gunicorn InvenTree.wsgi \
            --pythonpath ${pkg}/opt/inventree/src/backend/InvenTree
        '';
      };
    };

    systemd.sockets.inventree-gunicorn = {
      wantedBy = [ "sockets.target" ];
      partOf = [ "inventree.target" ];
      socketConfig.ListenStream = "/run/inventree/gunicorn.socket";
    };

    systemd.services.inventree-qcluster = {
      description = "InvenTree qcluster server";
      requiredBy = [ "inventree.target" ];
      wantedBy = [ "inventree.target" ];
      partOf = [ "inventree.target" ];
      environment = env;
      serviceConfig = {
        User = "inventree";
        Group = "inventree";
        StateDirectory = "inventree";
        #RuntimeDirectory = "inventree";
        PrivateTmp = true;
        ExecStart = ''
          ${pkg}/opt/inventree/src/backend/InvenTree/manage.py qcluster
        '';
      };
    };
  };
}
