{ config, pkgs, lib, inputs, ... }:
let
  cfg = config.persist;

  takeAll = what: builtins.concatMap (x: x.${what});

  persists = with cfg; [ state derivative cache ];

  absoluteHomeFiles = map (x: "${cfg.homeDir}/${x}");

  allHomeFiles = takeAll "homeFiles" persists;

  absoluteEtcFiles = map (x: "/etc/${x}");

  allEtcFiles = absoluteEtcFiles (takeAll "etcFiles" persists);

  allDirectories = takeAll "directories" persists;
in {
  options = with lib;
    with types;
    let
      common = {
        directories = mkOption {
          type = listOf path;
          default = [ ];
        };
        etcFiles = mkOption {
          type = listOf str;
          default = [ ];
        };
        homeFiles = mkOption {
          type = listOf str;
          default = [ ];
        };
      };
    in {
      persist = {

        enable = mkEnableOption "a tmpfs root with explicit opt-in state";

        persistRoot = mkOption {
          type = path;
          default = "/persist";
        };

        homeDir = mkOption {
          type = path;
          default = "/home/balsoft";
        };

        # Stuff that matters
        # TODO backups of this stuff
        state = {
          # backup = {...};
        } // common;

        # Stuff that can be computed from declarative+state, but is never invalidated (so shouldn't be cleaned up)
        derivative = common;

        # Stuff that's just there to speed up the system
        # It's cleaned up regularly, to solve the cache invalidation problem once and for all
        cache = {
          clean = {
            enable = mkEnableOption "cleaning the cache files and directories";
            dates = mkOption {
              type = str;
              default = "weekly";
              description =
                "A systemd.time calendar description of when to clean the cache files";
            };
          };
        } // common;

      };
    };

  imports = [
    inputs.impermanence.nixosModules.impermanence
    # Eugh
    (let
      module = (import "${inputs.impermanence}/home-manager.nix" {
        inherit pkgs lib;
        config = lib.recursiveUpdate config.home-manager.users.balsoft {
          home.persistence."${cfg.persistRoot}${cfg.homeDir}" = {
            directories = [ ];
            files = allHomeFiles;
            allowOther = false;
            removePrefixDirectory = false;
          };
        };
      });
    in {
      config.home-manager.users.balsoft = lib.mkIf cfg.enable module.config;
    })
  ];

  config = lib.mkIf cfg.enable {
    environment.persistence.${cfg.persistRoot} = {
      directories = allDirectories;
      files = allEtcFiles;
    };

    fileSystems."/" = {
      device = "none";
      options = [ "defaults" "size=4G" "mode=755" ];
      fsType = "tmpfs";
    };

    boot.initrd.postMountCommands = assert
      config.fileSystems ? ${cfg.persistRoot}
      && config.fileSystems.${cfg.persistRoot}.neededForBoot;
      ''
        mkdir -p /mnt-root/nix
        mount --bind /mnt-root${cfg.persistRoot}/nix /mnt-root/nix
        chmod 755 /mnt-root
      '';

    # Euuuugh
    systemd.services.persist-cache-cleanup = lib.mkIf cfg.cache.clean.enable {
      description = "Cleaning up cache files and directories";
      script = ''
        ${builtins.concatStringsSep "\n" (map (x: "rm ${lib.escapeShellArg x}")
          (absoluteEtcFiles cfg.cache.etcFiles
            ++ absoluteHomeFiles cfg.cache.homeFiles))}

        ${builtins.concatStringsSep "\n"
        (map (x: "rm -rf ${lib.escapeShellArg x}") cfg.cache.directories)}
      '';
      startAt = cfg.cache.clean.dates;
    };

  };
}
