# heavily based on https://discourse.nixos.org/t/gitea-nix-actions-runner-setup/35279
{
  config,
  inputs',
  pkgs,
  lib,
  ...
}: let
  storeDeps = pkgs.runCommand "store-deps" {} ''
    mkdir -p $out/bin
    for dir in ${toString [pkgs.coreutils pkgs.findutils pkgs.gnugrep pkgs.gawk pkgs.git pkgs.nix pkgs.bash pkgs.jq pkgs.nodejs inputs'.nix-fast-build.packages.nix-fast-build pkgs.curl pkgs.tea]}; do
      for bin in "$dir"/bin/*; do
        ln -s "$bin" "$out/bin/$(basename "$bin")"
      done
    done

    # Add SSL CA certs
    mkdir -p $out/etc/ssl/certs
    cp -a "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" $out/etc/ssl/certs/ca-bundle.crt
  '';
  numInstances = 1;
  pushToCache = pkgs.writeScript "push-to-cache.sh" ''
    #!/bin/sh
    set -eu
    set -f # disable globbing
    export IFS=' '

    echo "Uploading paths" $OUT_PATHS
    exec nix copy --to "s3://cache?profile=default&region=eu-south-1&scheme=https&endpoint=cache.aciceri.dev" $OUT_PATHS
  '';
in
  lib.mkMerge [
    {
      # everything here has no dependencies on the store
      systemd.services.gitea-runner-nix-image = {
        wantedBy = ["multi-user.target"];
        after = ["podman.service"];
        requires = ["podman.service"];
        path = [config.virtualisation.podman.package pkgs.gnutar pkgs.shadow pkgs.getent];
        # we also include etc here because the cleanup job also wants the nixuser to be present
        script = ''
          set -eux -o pipefail
          mkdir -p etc/nix

          # Create an unpriveleged user that we can use also without the run-as-user.sh script
          touch etc/passwd etc/group
          groupid=$(cut -d: -f3 < <(getent group nixuser))
          userid=$(cut -d: -f3 < <(getent passwd nixuser))
          groupadd --prefix $(pwd) --gid "$groupid" nixuser
          emptypassword='$6$1ero.LwbisiU.h3D$GGmnmECbPotJoPQ5eoSTD6tTjKnSWZcjHoVTkxFLZP17W9hRi/XkmCiAMOfWruUwy8gMjINrBMNODc7cYEo4K.'
          useradd --prefix $(pwd) -p "$emptypassword" -m -d /tmp -u "$userid" -g "$groupid" -G nixuser nixuser

          echo -n "access-tokens = " > etc/nix/access-tokens
          cat ${config.age.secrets.forgejo-nix-access-tokens.path} >> etc/nix/access-tokens

          cat <<NIX_CONFIG > etc/nix/nix.conf
          accept-flake-config = true
          experimental-features = nix-command flakes
          post-build-hook = ${pushToCache}
          include access-tokens
          NIX_CONFIG

          cat <<NSSWITCH > etc/nsswitch.conf
          passwd:    files mymachines systemd
          group:     files mymachines systemd
          shadow:    files

          hosts:     files mymachines dns myhostname
          networks:  files

          ethers:    files
          services:  files
          protocols: files
          rpc:       files
          NSSWITCH

          # list the content as it will be imported into the container
          tar -cv . | tar -tvf -
          tar -cv . | podman import - gitea-runner-nix
        '';
        serviceConfig = {
          RuntimeDirectory = "gitea-runner-nix-image";
          WorkingDirectory = "/run/gitea-runner-nix-image";
          Type = "oneshot";
          RemainAfterExit = true;
        };
      };

      users.users.nixuser = {
        group = "nixuser";
        description = "Used for running nix ci jobs";
        home = "/var/empty";
        isSystemUser = true;
        # extraGroups = [ "podman" ];
      };
      users.groups.nixuser = {};
    }
    {
      # Format of the token file:
      virtualisation = {
        podman.enable = true;
      };

      # virtualisation.containers.storage.settings = {
      #   storage.driver = "zfs";
      #   storage.graphroot = "/var/lib/containers/storage";
      #   storage.runroot = "/run/containers/storage";
      #   storage.options.zfs.fsname = "zroot/root/podman";
      # };

      # virtualisation.containers.containersConf.settings = {
      #   # podman seems to not work with systemd-resolved
      #   containers.dns_servers = [ "8.8.8.8" "8.8.4.4" ];
      # };
    }
    {
      systemd.services = lib.genAttrs (builtins.genList (n: "gitea-runner-nix${builtins.toString n}") numInstances) (name: {
        # TODO: systemd confinment
        serviceConfig = {
          # Hardening (may overlap with DynamicUser=)
          # The following options are only for optimizing output of systemd-analyze
          AmbientCapabilities = "";
          CapabilityBoundingSet = "";
          # ProtectClock= adds DeviceAllow=char-rtc r
          DeviceAllow = "";
          NoNewPrivileges = true;
          PrivateDevices = true;
          PrivateMounts = true;
          PrivateTmp = true;
          PrivateUsers = true;
          ProtectClock = true;
          ProtectControlGroups = true;
          ProtectHome = true;
          ProtectHostname = true;
          ProtectKernelLogs = true;
          ProtectKernelModules = true;
          ProtectKernelTunables = true;
          ProtectSystem = "strict";
          RemoveIPC = true;
          RestrictNamespaces = true;
          RestrictRealtime = true;
          RestrictSUIDSGID = true;
          UMask = "0066";
          ProtectProc = "invisible";
          SystemCallFilter = [
            "~@clock"
            "~@cpu-emulation"
            "~@module"
            "~@mount"
            "~@obsolete"
            "~@raw-io"
            "~@reboot"
            "~@swap"
            # needed by go?
            #"~@resources"
            "~@privileged"
            "~capset"
            "~setdomainname"
            "~sethostname"
          ];
          RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK"];

          # Needs network access
          PrivateNetwork = false;
          # Cannot be true due to Node
          MemoryDenyWriteExecute = false;

          # The more restrictive "pid" option makes `nix` commands in CI emit
          # "GC Warning: Couldn't read /proc/stat"
          # You may want to set this to "pid" if not using `nix` commands
          ProcSubset = "all";
          # Coverage programs for compiled code such as `cargo-tarpaulin` disable
          # ASLR (address space layout randomization) which requires the
          # `personality` syscall
          # You may want to set this to `true` if not using coverage tooling on
          # compiled code
          LockPersonality = false;

          # Note that this has some interactions with the User setting; so you may
          # want to consult the systemd docs if using both.
          DynamicUser = true;
        };
      });

      services.gitea-actions-runner = {
        package = pkgs.forgejo-actions-runner;
        instances = lib.genAttrs (builtins.genList (n: "nix${builtins.toString n}") numInstances) (name: {
          enable = true;
          name = "nix-runner";
          # take the git root url from the gitea config
          # only possible if you've also configured your gitea though the same nix config
          # otherwise you need to set it manually
          url = "https://git.aciceri.dev";
          # use your favourite nix secret manager to get a path for this
          tokenFile = config.age.secrets.forgejo-runners-token.path;
          labels = ["nix:docker://gitea-runner-nix"];
          settings = {
            container.options = "-e NIX_BUILD_SHELL=/bin/bash -e PAGER=cat -e PATH=/bin -e SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt --device /dev/kvm -v /nix:/nix -v ${storeDeps}/bin:/bin -v ${storeDeps}/etc/ssl:/etc/ssl --user nixuser --device=/dev/kvm";
            # the default network that also respects our dns server settings
            container.network = "host";
            container.valid_volumes = [
              "/nix"
              "${storeDeps}/bin"
              "${storeDeps}/etc/ssl"
            ];
          };
        });
      };
    }
  ]
