{
  config,
  pkgs,
  lib,
  ...
}:
let
  mainCfg = config.services.drumknotty;
  cfg = config.services.drumknotty.worblehat;

  format = pkgs.formats.toml { };
in
{
  options.services.drumknotty.worblehat = {
    enable = lib.mkEnableOption "";

    package = lib.mkPackageOption pkgs "worblehat" { };

    settings = lib.mkOption {
      description = "Configuration for worblehat";
      default = { };
      type = lib.types.submodule {
        freeformType = format.type;
      };
    };

    createLocalDatabase = lib.mkEnableOption "" // {
      description = ''
        Whether to set up a local postgres database automatically.

        ::: {.note}
        You must set up postgres manually before enabling this option.
        :::
      '';
    };

    deadline-daemon = {
      enable = lib.mkEnableOption "" // {
        description = ''
          Whether to enable the worblehat deadline-daemon service,
          which periodically checks for upcoming deadlines and notifies users.

          Note that this service is independent of the main worblehat service,
          and must be enabled separately.
        '';
      };

      onCalendar = lib.mkOption {
        type = lib.types.str;
        description = ''
          How often to trigger rendering the map,
          in the format of a systemd timer onCalendar configuration.

          See {manpage}`systemd.timer(5)`.
        '';
        default = "*-*-* 10:15:00";
      };
    };
  };

  config = lib.mkMerge [
    {
      assertions = [
        {
          assertion = cfg.createLocalDatabase -> config.services.postgresql.enable;
          message = "PostgreSQL must be enabled for worblehat to create a local database";
        }
      ];

      # TODO: Retrieve defaults from the example config file in the project code.
      services.drumknotty.worblehat.settings = {
        logging = {
          debug = lib.mkDefault true;
          debug_sql = lib.mkDefault false;
        };

        database = {
          type = lib.mkDefault "sqlite";
          sqlite.path = lib.mkDefault "./worblehat.sqlite";
          postgresql = {
            host = lib.mkDefault "localhost";
            port = lib.mkDefault 5432;
            username = lib.mkDefault "worblehat";
            password = lib.mkDefault "/var/lib/worblehat/db-password";
            database = lib.mkDefault "worblehat";
          };
        };

        flask = {
          TESTING = lib.mkDefault true;
          DEBUG = lib.mkDefault true;
          FLASK_ENV = lib.mkDefault "development";
          SECRET_KEY = lib.mkDefault "change-me";
        };

        smtp = {
          enabled = lib.mkDefault false;
          host = lib.mkDefault "smtp.pvv.ntnu.no";
          port = lib.mkDefault 587;
          username = lib.mkDefault "worblehat";
          password = lib.mkDefault "/var/lib/worblehat/smtp-password";
          from = lib.mkDefault "worblehat@pvv.ntnu.no";
          subject_prefix = lib.mkDefault "[Worblehat]";
        };

        deadline_daemon = {
          enabled = lib.mkDefault true;
          dryrun = lib.mkDefault false;
          warn_days_before_borrowing_deadline = lib.mkDefault [
            5
            1
          ];
          days_before_queue_position_expires = lib.mkDefault 14;
          warn_days_before_expiring_queue_position_deadline = lib.mkDefault [
            3
            1
          ];
        };
      };

      systemd.services."drumknotty-screen-session".serviceConfig.StateDirectory = [
        "worblehat"
      ];
    }

    (lib.mkIf ((mainCfg.enable && cfg.enable) || cfg.deadline-daemon.enable) {
      environment.systemPackages = [ cfg.package ];
      environment.etc."worblehat/config.toml".source = format.generate "worblehat-config.toml" cfg.settings;
    })

    (lib.mkIf (mainCfg.enable && cfg.enable) {
      services.drumknotty.worblehat.settings.general = lib.mkIf mainCfg.kioskMode {
        quit_allowed = false;
        stop_allowed = false;
      };

      services.drumknotty.worblehat.settings.database = lib.mkIf cfg.createLocalDatabase {
        type = "postgresql";
        postgresql.host = "/run/postgresql";
      };

      services.postgresql = lib.mkIf cfg.createLocalDatabase {
        authentication = ''
          local ${cfg.settings.database.postgresql.database} ${cfg.settings.database.postgresql.username} peer map=${cfg.settings.database.postgresql.username}
        '';
        identMap = ''
          ${cfg.settings.database.postgresql.username} drumknotty ${cfg.settings.database.postgresql.username}
        '';
        ensureDatabases = [ cfg.settings.database.postgresql.database ];
        ensureUsers = [{
          name = cfg.settings.database.postgresql.username;
          ensureDBOwnership = true;
          ensureClauses.login = true;
        }];
      };

      systemd.services.worblehat-setup-database = lib.mkIf cfg.createLocalDatabase {
        description = "Worblehat database setup";

        wantedBy = [ "default.target" ];
        requiredBy = [ "drumknotty-screen-session.service" ];
        before = [ "drumknotty-screen-session.service" ];
        after = [ "postgresql.service" ];

        unitConfig = {
          ConditionPathExists = "!/var/lib/worblehat/.db-setup-done";
        };
        serviceConfig = {
          Type = "oneshot";
          ExecStart = "${lib.getExe cfg.package} --config /etc/worblehat/config.toml create-db";
          ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/worblehat/.db-setup-done";
          StateDirectory = "worblehat";

          User = "drumknotty";
          Group = "drumknotty";
        };
      };
    })

    (lib.mkIf cfg.deadline-daemon.enable {
      systemd.timers.worblehat-deadline-daemon = lib.mkIf cfg.deadline-daemon.enable {
        description = "Worblehat Deadline Daemon";
        wantedBy = [ "timers.target" ];
        timerConfig = {
          OnCalendar = cfg.deadline-daemon.onCalendar;
          Persistent = true;
        };
      };

      systemd.services.worblehat-deadline-daemon = lib.mkIf cfg.deadline-daemon.enable {
        description = "Worblehat Deadline Daemon";
        wantedBy = [ "multi-user.target" ];
        after = [ "network.target" ];
        serviceConfig = {
          Type = "oneshot";
          CPUSchedulingPolicy = "idle";
          IOSchedulingClass = "idle";

          ExecStart =
            let
              worblehatArgs = lib.cli.toCommandLineShellGNU { } {
                config = "/etc/worblehat/config.toml";
              };
            in
            "${lib.getExe cfg.package} ${worblehatArgs} deadline-daemon";

          User = "drumknotty";
          Group = "drumknotty";
        };
      };
    })
  ];
}
