{ config, options, pkgs, lib, flake, flakeInputs, ... }:

with lib;

let
  cfg = config.nfsroot;
in {
  imports = with flake.nixosModules.systemProfiles; [
    tmpfs-root
  ];

  options = {
    nfsroot = {
      storeDevice = mkOption {
        type = types.str;
        default = "nfsroot:nix-store";
        description = "Nix store device";
      };

      registrationUrl = mkOption {
        type = types.str;
        default = "http://nfsroot/nix-registration";
        description = "Url of nix store registrations";
      };
    };

    system.build = {
      storeContents = mkOption {
        description = "Contents of nix store";
      };
    };
  };

  config = foldr recursiveUpdate {} ([
    {
      # Don't build the GRUB menu builder script, since we don't need it
      # here and it causes a cyclic dependency.
      boot.loader.grub.enable = false;

      # !!! Hack - attributes expected by other modules.
      environment.systemPackages = [ pkgs.grub2_efi ]
        ++ (if pkgs.stdenv.hostPlatform.system == "aarch64-linux"
            then []
            else [ pkgs.grub2 pkgs.syslinux ]);

      # In stage 1, mount a tmpfs on top of /nix/store (the squashfs
      # image) to make this a live CD.
      fileSystems."/nix/.ro-store" = mkImageMediaOverride
        { fsType = "nfs4";
          device = cfg.storeDevice;
          options = [ "ro" ];
          neededForBoot = true;
        };

      fileSystems."/nix/.rw-store" = mkImageMediaOverride
        { fsType = "tmpfs";
          options = [ "mode=0755" ];
          neededForBoot = true;
        };

      fileSystems."/nix/store" = mkImageMediaOverride
        { fsType = "overlay";
          device = "overlay";
          options = [
            "lowerdir=/nix/.ro-store"
            "upperdir=/nix/.rw-store/store"
            "workdir=/nix/.rw-store/work"
          ];

          depends = [
            "/nix/.ro-store"
            "/nix/.rw-store/store"
            "/nix/.rw-store/work"
          ];
        };

      nix.settings.use-sqlite-wal = false;

      boot.initrd.availableKernelModules = [ "nfs" "nfsv4" "overlay" ];
      boot.initrd.supportedFilesystems = [ "nfs" "nfsv4" "overlay" ];
      services.rpcbind.enable = mkImageMediaOverride false;

      boot.initrd.network.enable = true;
      boot.initrd.network.flushBeforeStage2 = false; # otherwise nfs doesn't work
      boot.initrd.postMountCommands = ''
        mkdir -p /mnt-root/etc/
        cp /etc/resolv.conf /mnt-root/etc/resolv.conf
      '';
      networking.useDHCP = true;
      networking.resolvconf.enable = false;
      networking.dhcpcd.persistent = true;


      system.build.storeContents = [config.system.build.toplevel];

      system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
        #!ipxe
        # Use the cmdline variable to allow the user to specify custom kernel params
        # when chainloading this script from other iPXE scripts like netboot.xyz
        kernel ${pkgs.stdenv.hostPlatform.linux-kernel.target} init=${config.system.build.toplevel}/init initrd=initrd ${toString config.boot.kernelParams} ''${cmdline}
        initrd initrd
        boot
      '';

      boot.postBootCommands =
        ''
          # After booting, register the contents of the Nix store on NFS
          # in the Nix database in the tmpfs.
          ${pkgs.curl}/bin/curl ${escapeShellArg cfg.registrationUrl} | ${config.nix.package.out}/bin/nix-store --load-db

          # nixos-rebuild also requires a "system" profile and an
          # /etc/NIXOS tag.
          touch /etc/NIXOS
          ${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
        '';

      boot.initrd.systemd.enable = false;
    }
  ] ++ (optional (options ? system.etc) {
    system.etc.overlay.enable = false;
  }) ++ (optional (options ? system.sysusers) {
    systemd.sysusers.enable = false;
  }));
}