{ lib, pkgs, config, systems, ... }:
let
  inherit (builtins) head attrNames;
  inherit (lib) mkMerge mkIf mkDefault optionalAttrs mapAttrs' optionalString;
  inherit (lib.my) mkOpt' mkBoolOpt';

  cfg = config.my.deploy;

  keepGensOpt = with lib.types; mkOpt' ints.unsigned 10
    "Number of generations to keep when cleaning up old deployments (0 to disable deletion on deployment).";
  keepGensSnippet = p: n: optionalString (n > 0) ''
    ${config.nix.package}/bin/nix-env -p "${p}" --delete-generations +${toString n}
  '';

  # Based on https://github.com/serokell/deploy-rs/blob/master/flake.nix
  nixosActivate = cfg': base: (pkgs.deploy-rs.lib.activate.custom // {
    dryActivate = "$PROFILE/bin/switch-to-configuration dry-activate";
    boot = ''
      $PROFILE/bin/switch-to-configuration boot

      ${keepGensSnippet "$PROFILE" cfg'.keepGenerations}
    '';
  }) base.config.system.build.toplevel ''
    # work around https://github.com/NixOS/nixpkgs/issues/73404
    cd /tmp

    "$PROFILE"/bin/switch-to-configuration switch

    # https://github.com/serokell/deploy-rs/issues/31
    ${with base.config.boot.loader;
    optionalString systemd-boot.enable
    "sed -i '/^default /d' ${efi.efiSysMountPoint}/loader/loader.conf"}

    ${keepGensSnippet "$PROFILE" cfg'.keepGenerations}
  '';

  systemdUtil = pkgs.writeShellApplication {
    name = "systemd-util.sh";
    text = ''
      svcActionWatch() {
        action="$1"
        shift
        unit="$1"
        shift

        journalctl -o cat --no-pager -n 0 -f -u "$unit" &
        jPid=$!
        cleanup() {
          # shellcheck disable=SC2317
          kill "$jPid"
        }
        trap cleanup EXIT

        systemctl "$@" "$action" "$unit"
      }
    '';
  };

  ctrProfiles = optionalAttrs cfg.generate.containers.enable (mapAttrs' (n: c:
  let
    ctrConfig = systems."${n}".configuration.config;
  in
  {
    name = "container-${n}";
    value = {
      path = (pkgs.deploy-rs.lib.activate.custom // {
        boot = ''
          echo "Next systemd-nspawn@${n}.service restart / reload will load config"
        '';
      }) ctrConfig.my.buildAs.container ''
        source ${systemdUtil}/bin/systemd-util.sh
        ${if c.hotReload then ''
          if (! systemctl show -p ActiveState systemd-nspawn@${n} | grep -q "ActiveState=active") || \
            systemctl show -p StatusText systemd-nspawn@${n} | grep -q "Dummy container"; then
            action=restart
          else
            action=reload
          fi

          svcActionWatch "$action" systemd-nspawn@${n}
        '' else ''
          svcActionWatch restart systemd-nspawn@${n}
        ''}

        ${keepGensSnippet "$PROFILE" cfg.generate.containers.keepGenerations}
      '';
      profilePath = "/nix/var/nix/profiles/per-container/${n}/system";

      user = "root";
    };
  }) config.my.containers.instances);
in
{
  options.my.deploy = with lib.types; {
    authorizedKeys = {
      keys = mkOpt' (listOf singleLineStr) [ ] "SSH public keys to add to the default deployment user.";
      keyFiles = mkOpt' (listOf path) [ lib.my.c.sshKeyFiles.deploy ] "SSH public key files to add to the default deployment user.";
    };

    enable = mkBoolOpt' true "Whether to expose deploy-rs configuration for this system.";
    inherit (lib.my.deploy-rs) node;

    generate = {
      system = {
        enable = mkBoolOpt' true "Whether to generate a deploy-rs profile for this system's config.";
        mode = mkOpt' str "switch" "switch-to-configuration mode.";
        keepGenerations = keepGensOpt;
      };
      containers = {
        enable = mkBoolOpt' true "Whether to generate deploy-rs profiles for this system's containers.";
        keepGenerations = keepGensOpt;
      };
    };
  };

  config = mkMerge [
    {
      my.deploy.enable = mkIf (config.my.build.isDevVM || config.boot.isContainer) false;
    }
    (mkIf cfg.enable {
      my.deploy.node = {
        hostname = mkDefault config.networking.fqdn;
        profilesOrder = [ "system" ] ++ (attrNames ctrProfiles);
        profiles = {
          system = mkIf cfg.generate.system.enable {
            path = nixosActivate cfg.generate.system { inherit config; };

            user = "root";
          };
        } // ctrProfiles;

        sshUser = "deploy";
        user = mkDefault "root";
        sudo = mkDefault (if config.security.doas.enable then "doas -u" else "sudo -u");
        sshOpts = mkDefault [ "-p" (toString (head config.services.openssh.ports)) ];
      };

      users = {
        users."${cfg.node.sshUser}" = {
          isSystemUser = true;
          group = cfg.node.sshUser;
          extraGroups = mkDefault [ "wheel" ];
          shell = pkgs.bash;
          openssh.authorizedKeys = cfg.authorizedKeys;
        };
        groups."${cfg.node.sshUser}" = {};
      };
    })
  ];
}