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

{
  options.c3d2.autoUpdate = with lib; mkOption {
    description = ''
      Enables a timer that periodically checks hydra.hq.c3d2.de for the last build of the local system, and switches to it if it is different.

      Also enables periodical /nix/store GC.
    '';
    type = types.bool;
    default = false;
  };

  config = {
    # the presence of this .service file signifies that the system is
    # autoupdate-enabled. it is checked to prevent autoupdating back
    # to a system without autoupdate when deploying with autoupdate
    # for the first time.
    systemd.services.autoupdate = lib.mkIf config.c3d2.autoUpdate {
      wantedBy = [ "multi-user.target" ];
      path = with pkgs; [ nix nettools curl jq ];
      serviceConfig = {
        Type = "oneshot";
        # switch-to-configuration may not return. HACK: cap running
        # time so that the timer can be scheduled again.
        TimeoutStartSec = "30min";
      };
      script = ''
        OLD=$(readlink /run/current-system)
        echo Current system: $(basename $OLD)
        NEW=$(curl -sLH "Accept: application/json" https://hydra.hq.c3d2.de/job/c3d2/nix-config/${config.networking.hostName}/latest | jq -er .buildoutputs.out.path)
        if [ -z "$NEW" ] || [ "$NEW" = "null" ]; then
          echo "Unable to obtain updated system"
          exit 1
        fi
        echo New system: $(basename $NEW)

        if [ "$OLD" != "$NEW" ]; then
          echo "Fetching new system built by https://hydra.hq.c3d2.de/jobset/c3d2/nix-config"
          # this should fetch the new system from the binary cache
          nix copy --from https://nix-cache.hq.c3d2.de "$NEW"
          if [ -e "$NEW/etc/systemd/system/autoupdate.timer" ]; then
            echo "Switch to the new system..."
            nix-env -p /nix/var/nix/profiles/system --set $NEW
            "$NEW/bin/switch-to-configuration" switch
          else
            echo "New system is not yet autoupdate-enabled. Refusing to switch into a dead end."
          fi
        else
          echo "No update required"
        fi
      '';
      # don't let the switch kill this service, aborting the switch
      restartIfChanged = false;
      unitConfig.X-StopOnRemoval = false;
      # create timer
      startAt = "hourly";
    };

    nix = {
      # Show a diff when activating a new system except for microvms which handle this seperately
      diffSystem = config.c3d2.deployment.server or "" == "";
      gc = lib.mkIf config.c3d2.autoUpdate {
        automatic = true;
        randomizedDelaySec = "6h";
        options = "--delete-older-than 21d";
      };
    };

    environment.systemPackages = [ (
      # Provide a manual updating script that fetches the latest
      # updated+built system from Hydra
      pkgs.writeScriptBin "update-from-hydra" ''
        #! ${pkgs.runtimeShell} -e

        OLD=$(readlink /run/current-system)
        echo Current system: $(basename $OLD)
        NEW=$(curl -sLH "Accept: application/json" https://hydra.hq.c3d2.de/job/c3d2/nix-config/${config.networking.hostName}/latest | ${pkgs.jq}/bin/jq -er .buildoutputs.out.path)
        if [ -z "$NEW" ] || [ "$NEW" = "null" ]; then
          echo "Unable to obtain updated system"
          exit 1
        fi
        echo New system: $(basename $NEW)

        if [ "$OLD" != "$NEW" ]; then
          echo "Fetching new system built by https://hydra.hq.c3d2.de/jobset/c3d2/nix-config"
          # this should fetch the new system from the binary cache
          nix copy --from https://nix-cache.hq.c3d2.de "$NEW"
          echo "Switch to the new system..."
          nix-env -p /nix/var/nix/profiles/system --set $NEW
          "$NEW/bin/switch-to-configuration" switch
        else
          echo "No update required"
        fi
      ''
    ) ];
  };
}
