{ config, pkgs, hostName, lib, ... }:
with lib;
let
  cfg = config.system.rebuild-machine;
  sshConfig = pkgs.writeText "config" ''
    UserKnownHostsFile ${knownHostsFile}
    Host ${cfg.repoHost}
      User ${cfg.repoUser}
      IdentityFile ${if isNull cfg.sopsConfig then cfg.repoPrivkey else config.sops.secrets."${cfg.sopsName}".path}
      IdentitiesOnly yes
  '';
  knownHostsFile = pkgs.writeText "known_hosts" (concatMapStringsSep "\n" (kPath: cfg.repoHost + " " + readFile kPath) (attrValues cfg.repoPubkeys));
  rebuildScript = pkgs.stdenv.mkDerivation {
    name = "rebuild-${hostName}";
    src = ./rebuild-machine.zsh;
    buildInputs = with pkgs; [ makeWrapper ];
    phases = [ "buildPhase" "installPhase" ];
    buildPhase = ''
      substitute $src rebuild-machine.zsh \
        --subst-var-by zsh ${pkgs.zsh} \
        --subst-var-by coreutils ${pkgs.coreutils} \
        --subst-var-by openssh ${pkgs.openssh} \
        --subst-var-by wrapperDir ${config.security.wrapperDir} \
        --subst-var-by sshConfig ${sshConfig} \
        --subst-var-by out "$out" \
        --subst-var-by nixosRebuild ${config.system.build.nixos-rebuild} \
        --subst-var-by flake ${cfg.flake.name} \
        --subst-var-by scriptName ${cfg.scriptName} \
        --subst-var-by flakeOutput ${cfg.flake.flakeOutput}
    '';
    installPhase = ''
      mkdir -p $out/bin
      install -m 0755 rebuild-machine.zsh $out/bin/${cfg.scriptName}
    '';
  };
in {
  options = {
    system.rebuild-machine = {
      scriptName = mkOption {
        type = types.str;
        default = "rebuild-${hostName}";
        description = ''
          Name of the script wrapping nixos-rebuild
        '';
      };
      flake = mkOption {
        type = types.submodule {
          options = {
            flake = mkOption {
              type = types.attrs;
            };
            flakeOutput = mkOption {
              type = types.str;
            };
            name = mkOption {
              type = types.str;
              default = "machines";
            };
          };
        };
        default = { flake = { type = "git"; url = "ssh://${cfg.repoHost}/nixos"; ref = "flakes"; }; flakeOutput = hostName; };
        defaultText = literalExpression ''{ flake = { type = "git"; url = "ssh://''${config.system.rebuild-machine.repoHost}/nixos"; ref = "flakes"; }; flakeOutput = hostName; }'';
        description = ''
          The Flake URI of the NixOS configuration to build.
        '';
      };
      repoHost = mkOption {
        type = types.str;
        default = "git.yggdrasil.li";
      };
      repoUser = mkOption {
        type = types.str;
        default = "gitolite";
      };
      repoPubkeys = mkOption {
        type = types.attrsOf types.path;
        default = genAttrs ["rsa" "ed25519"] (kType: ./ssh-pub + "/${cfg.repoHost}-${kType}.pub");
        defaultText = literalExpression ''genAttrs ["rsa" "ed25519"] (kType: ./ssh-pub + "/''${cfg.repoHost}-''${kType}.pub")'';
      };
      repoPrivkey = mkOption {
        type = types.path;
        default = ./ssh + "/${hostName}/private";
        defaultText = literalExpression ''./ssh + "/''${hostName}/private"'';
      };
      sopsName = mkOption {
        type = types.nullOr types.str;
        default = "rebuild-machines";
      };
      sopsConfig = mkOption {
        type = types.nullOr types.attrs;
        default = {
          format = "binary";
        };
      };
      period = mkOption {
        type = types.nullOr types.str;
        description = "Call rebuild-${hostName} boot periodically";
        default = null;
        example = "hourly";
      };
    };
  };
  config = {
    assertions = [
      { assertion = isNull cfg.sopsConfig || (!(isNull cfg.sopsName));
        message = "If option sopsConfig is not null option sopsName may not be null";
      }
    ];
    sops.secrets = mkIf (!(isNull cfg.sopsConfig)) {
      "${cfg.sopsName}" = {
        sopsFile = cfg.repoPrivkey;
      } // cfg.sopsConfig;
    };
    environment.systemPackages = [rebuildScript];
    systemd.services."rebuild-${hostName}" = mkIf (cfg.period != null) {
      description = "Upgrade System on Next Boot";
      requisite = [ "network.target" ];
      after = [ "network.target" ];
      restartIfChanged = false;
      unitConfig.X-StopOnRemoval = false;
      serviceConfig = {
        Type = "oneshot";
        ExecStart = "${rebuildScript}/bin/${cfg.scriptName} boot";
      };
    };
    systemd.timers."rebuild-${hostName}" = mkIf (cfg.period != null) {
      wantedBy = [ "timers.target" ];
      timerConfig = {
        OnCalendar = cfg.period;
        Persistent = true;
      };
    };
    nix.registry = mkIf (cfg.flake != null) {
      ${cfg.flake.name} = {
        exact = false;
        to = cfg.flake.flake;
      };
    };
  };
}