{ lib
, name ? "chaos"
, disk ? "/dev/sda1"
, enableCeph ? true
, enableLuks ? true
, enableZfs ? true
, ...
}:

assert lib.assertMsg (enableCeph || enableZfs) "Must enable ceph or zfs!";
assert lib.assertMsg (enableCeph -> enableLuks) "Ceph requires Luks!";

{
  disko.devices =
    let
      rootSize = 200;
      zfs = {
        size = if (!enableCeph) then "100%FREE" else "${toString rootSize}GiB";
        content = {
          pool = name;
          type = "zfs";
        };
      };
    in
    {
      disk.${disk} = {
        device = disk;
        type = "disk";
        content = {
          type = "table";
          format = "gpt";
          partitions = lib.optional enableZfs
            {
              name = "ESP";
              start = "1MiB";
              end = "512MiB";
              bootable = true;
              content = {
                type = "filesystem";
                format = "fat32";
                mountpoint = "/boot";
              };
            } ++ [
            {
              name = "root";
              start = if enableZfs then "512MiB" else "1MiB";
              end = "100%";
              part-type = "primary";
              content = lib.optionalAttrs enableLuks
                {
                  type = "luks";
                  name = "crypt-${name}";
                  # TODO: add password, otherwise prompt opens
                  keyFile = "/$PWD/keyFile";
                  content = {
                    type = "lvm_pv";
                    vg = "lvm-${name}";
                  };
                } // lib.optionalAttrs (!enableLuks) zfs.content;
            }
          ];
        };
      };
    } // lib.optionalAttrs enableLuks {
      lvm_vg."lvm-${name}" = {
        type = "lvm_vg";
        lvs = lib.optionalAttrs enableCeph
          {
            # the header is 3650 byte long and substract an additional 446 byte for aligment
            # error messages:
            #   Volume group "lvm-chaos" has insufficient free space (51195 extents): 51200 required.
            #   Size is not a multiple of 512. Try using 40057405440 or 40057405952.
            ceph.size =
              let
                # convert GiB to bytes
                rootSizeMiB = rootSize * 1024 * 1024 * 1024;
                # convert back to MiB and allign to 4 MiB in the process
                roundToMiB = "/1024/1024/4*4";
                # substract 512 MiB for /boot and 20 MiB for luks+header+other
                bootOther = "-512-20";
              in
              "$((($(lsblk /dev/sda --noheadings --nodeps --output SIZE --bytes)-${toString rootSizeMiB})${roundToMiB}${bootOther}))MiB";
          } // lib.optionalAttrs enableZfs { inherit zfs; };
      };
    } // {
      zpool."${name}" = {
        type = "zpool";
        mountpoint = "none";
        mountRoot = "/mnt";
        rootFsOptions.acltype = "posixacl";
        options = {
          ashift = "12";
          autotrim = "on";
        };
        datasets =
          let
            dataset = mountpoint: {
              options = {
                canmount = "on";
                compression = "zstd";
                normalization = "formD";
                xattr = "sa";
                inherit mountpoint;
              };
              type = "zfs_fs";
            };
          in
          {
            "data" = dataset "/";
            "data/etc" = dataset "/etc";
            "data/home" = dataset "/home";
            "data/var" = dataset "/var";
            # used by services.postgresqlBackup and later by restic
            "data/var/backup" = dataset "/var/backup";
            "data/var/lib" = dataset "/var/lib";
            "data/var/log" = dataset "/var/log";
            "nixos" = {
              options = {
                canmount = "off";
                mountpoint = "none";
              };
              type = "zfs_fs";
            };
            "nixos/nix" = dataset "/nix";
            "nixos/nix/store" = {
              options = {
                atime = "off";
                canmount = "on";
                mountpoint = "/nix/store";
              };
              type = "zfs_fs";
            };
            "nixos/nix/var" = dataset "/nix/var";
            "reserved" = {
              # zfs uses copy on write and requires some free space to delete files when the disk is completely filled
              options = {
                canmount = "off";
                mountpoint = "none";
                reservation = "5GiB";
              };
              type = "zfs_fs";
            };
          };
      };
    };
}
