{ config, pkgs, ... }:

# TODO Note: One can specify ExecStart and ExecStop. Maybe to pause some script during shutdown/suspend? This would be nice for backups. Just finish the current snapshot then pause.

# TODO: https://unix.stackexchange.com/questions/619987/stop-systemd-service-before-suspend-start-again-after-resume

# Suspend, hibernate, shutdown
#
# Some of the upstream system units:
#   https://github.com/NixOS/nixpkgs/blob/5c43dee215c279dceee7861eec85862ad85cc330/nixos/modules/system/boot/systemd.nix#L104
#   - suspend.target
#   - hibernate.target
#   - sleep.target
#   - hybrid-sleep.target
#   https://github.com/NixOS/nixpkgs/blob/5c43dee215c279dceee7861eec85862ad85cc330/nixos/modules/system/boot/systemd.nix#L115
#   - reboot.target
#   - poweroff.target
#
# BTRFS scrub prevents suspend2ram and proper shutdown
# https://github.com/NixOS/nixpkgs/blob/5c43dee215c279dceee7861eec85862ad85cc330/nixos/modules/tasks/filesystems/btrfs.nix#L131
#   conflicts = [ "shutdown.target" "sleep.target" ];
#   before = [ "shutdown.target" "sleep.target" ];

# Benefits of Systemd Timers over cron: https://wiki.archlinux.org/title/Systemd/Timers#Benefits
# Timer example: https://nixos.wiki/wiki/Nix_Cookbook#Creating_periodic_services
# Timer example: https://nixos.wiki/wiki/Systemd/Timers

{
  systemd.timers."hello-world" = {
    # description = "My Timer";
    wantedBy = [ "timers.target" ];
    # If the service stopped or restarted, then this timer is stopped or restarted as well.
    partOf = [ "hello-world.service" ];
    # https://man.archlinux.org/man/systemd.timer.5
    timerConfig = {
      # Either calendar time.
      # Or relative time. https://unix.stackexchange.com/a/294200/315162
      #
      OnCalendar = "monthly";
      Persistent = true; # Catch up on missed runs of the service when the system was powered down. This setting only has an effect on timers configured with OnCalendar=.
      #
      #OnBootSec = "0m";
      #OnUnitActiveSec = "30d";
      #OnUnitInactiveSec = "30d";

      # To optimize power consumption, make sure to set this value as high as possible and as low as necessary.
      AccuracySec = "2d";
      # This setting is useful to stretch dispatching of similarly configured timer events over a certain time interval, to prevent them from firing all at the same time, possibly resulting in resource congestion.
      RandomizedDelaySec = "2d";

      # Takes a boolean argument. If true, an elapsing timer will cause the system to resume from suspend, should it be suspended and if the system supports this. Note that this option will only make sure the system resumes on the appropriate times, it will not take care of suspending it again after any work that is to be done is finished.
      # Note that this functionality requires privileges and is thus generally only available in the system service manager.
      #WakeSystem = true;
    };
  };

  systemd.services."hello-world" = {
    # description = "My Oneshot Service";

    # Prevent suspend2ram and proper shutdown.
    #
    # TODO Additionally:
    #   Option I:
    #     Use ExecStart and ExecStop. ExecStop initiates an early stop of the service and waits until this has finished.
    #     In `serviceConfig`, set `Type` to `simple` (and not `oneshot`, otherwise ExecStop is not used).
    #   Option II:
    #     Set `TimeoutSec` to `infinity`.
    #
    # If the specified units are started, then this unit is stopped and vice versa.
    conflicts = [ "shutdown.target" "sleep.target" ];
    # If this unit and one from `before` are being started,
    # the start-up of the latter is delayed until this service has finished starting up.
    before = [ "shutdown.target" "sleep.target" ];

    # https://man.archlinux.org/man/systemd.service.5.en#OPTIONS
    #   More options are documented e.g. here:
    #     https://man.archlinux.org/man/systemd.exec.5.en#SCHEDULING
    #     https://man.archlinux.org/man/systemd.exec.5.en#USER/GROUP_IDENTITY
    #
    # Example: https://github.com/NixOS/nixpkgs/blob/e9b4b56e5a20ac322c0c01ccab7ec697ab076ea0/nixos/modules/tasks/filesystems/btrfs.nix#L132-L142
    serviceConfig = {
      Type = "oneshot";

      # Takes a boolean value that specifies whether the service shall be considered active even when all its processes exited.
      # Defaults to `false`.
      #RemainAfterExit = true;

      # A shorthand for configuring both TimeoutStartSec= and TimeoutStopSec= to the specified value.
      # TimeoutStopSec:
      #   If no ExecStop= commands are specified, the service gets the SIGTERM immediately.
      #   It configures the time to wait for the service itself to stop.
      #   If it doesn't terminate in the specified time, it will be forcibly terminated by SIGKILL (see KillMode).
      # SIGTERM and SIGKILL:
      #   The SIGTERM signal is a generic signal used to cause program termination.
      #   Unlike SIGKILL, this signal can be blocked, handled, and ignored.
      #   It is the normal way to politely ask a program to terminate.
      # SIGINT:
      #   The SIGINT (“program interrupt”) signal is sent when the user types the INTR character (normally C-c).
      # Example:
      #   grow-partition.nix conflicts with `shutdown.target` and has an infinite `TimeoutStopSec` to make sure that resizing a partition is not interrupted.
      #   https://github.com/NixOS/nixpkgs/blob/008d84ab67c4f4ccbd7c0996005e46c8bd32d675/nixos/modules/system/boot/grow-partition.nix#L34
      #TimeoutSec = "infinity";

      PrivateTmp = true;

      #User = "myuser";

      # Lowest scheduling priority.
      Nice = 19;
      # Takes one of the strings realtime, best-effort or idle.
      IOSchedulingClass = "idle";

      #ExecStart = "${pkgs.coreutils}/bin/echo 'Hello World'";
      # Commands to execute to stop the service started via ExecStart=.
      # The command that asks the service to stop should wait for it to do so!
      # After the commands configured in this option are run, it is implied that the service is stopped, and any processes remaining for it are terminated.
      #
      # If this option is not specified, the process is terminated when service stop is requested.
      #ExecStop  = pkgs.writeShellScript "cancel-hello-world" ''
      #  set -eu -o pipefail
      #  echo 'Cancelling execution of hello-world'
      #'';
    };

    # Packages required for the script.
    #path = with pkgs; [
    #  openssh
    #  wget
    #  curl
    #];

    #environment = {
    #  MY_SERVICE_HOME = "/my/path/here";
    #  MY_SERVICE_MAX_CONNS = toString myVar;
    #};

    # Shell commands executed as the service's main process.
    script = ''
      set -eu -o pipefail
      ${pkgs.coreutils}/bin/echo 'Hello World'
    '';
  };
}
