{
  config,
  lib,
  pkgs,
  ...
}:

{
  imports = [
    (lib.mkRenamedOptionModule
      [ "virtualisation" "containers" "cdi" "dynamic" "nvidia" "enable" ]
      [ "hardware" "nvidia-container-toolkit" "enable" ]
    )
  ];

  options =
    let
      mountType = {
        options = {
          hostPath = lib.mkOption {
            type = lib.types.str;
            description = "Host path.";
          };
          containerPath = lib.mkOption {
            type = lib.types.str;
            description = "Container path.";
          };
          mountOptions = lib.mkOption {
            default = [
              "ro"
              "nosuid"
              "nodev"
              "bind"
            ];
            type = lib.types.listOf lib.types.str;
            description = "Mount options.";
          };
        };
      };
    in
    {

      hardware.nvidia-container-toolkit = {
        enable = lib.mkOption {
          default = false;
          type = lib.types.bool;
          description = ''
            Enable dynamic CDI configuration for Nvidia devices by running
            nvidia-container-toolkit on boot.
          '';
        };

        mounts = lib.mkOption {
          type = lib.types.listOf (lib.types.submodule mountType);
          default = [ ];
          description = "Mounts to be added to every container under the Nvidia CDI profile.";
        };

        mount-nvidia-executables = lib.mkOption {
          default = true;
          type = lib.types.bool;
          description = ''
            Mount executables nvidia-smi, nvidia-cuda-mps-control, nvidia-cuda-mps-server,
            nvidia-debugdump, nvidia-powerd and nvidia-ctk on containers.
          '';
        };

        device-name-strategy = lib.mkOption {
          default = "index";
          type = lib.types.enum [
            "index"
            "uuid"
            "type-index"
          ];
          description = ''
            Specify the strategy for generating device names,
            passed to `nvidia-ctk cdi generate`. This will affect how
            you reference the device using `nvidia.com/gpu=` in
            the container runtime.
          '';
        };

        mount-nvidia-docker-1-directories = lib.mkOption {
          default = true;
          type = lib.types.bool;
          description = ''
            Mount nvidia-docker-1 directories on containers: /usr/local/nvidia/lib and
            /usr/local/nvidia/lib64.
          '';
        };

        package = lib.mkPackageOption pkgs "nvidia-container-toolkit" { };
      };

    };

  config = lib.mkIf config.hardware.nvidia-container-toolkit.enable {
    assertions = [
      {
        assertion =
          config.hardware.nvidia.datacenter.enable || lib.elem "nvidia" config.services.xserver.videoDrivers;
        message = ''`nvidia-container-toolkit` requires nvidia datacenter or desktop drivers: set `hardware.nvidia.datacenter.enable` or add "nvidia" to `services.xserver.videoDrivers`'';
      }
    ];

    virtualisation.docker = {
      daemon.settings = lib.mkIf (lib.versionAtLeast config.virtualisation.docker.package.version "25") {
        features.cdi = true;
      };

      rootless.daemon.settings =
        lib.mkIf
          (
            config.virtualisation.docker.rootless.enable
            && (lib.versionAtLeast config.virtualisation.docker.package.version "25")
          )
          {
            features.cdi = true;
          };
    };

    hardware = {
      graphics.enable = lib.mkIf (!config.hardware.nvidia.datacenter.enable) true;

      nvidia-container-toolkit.mounts =
        let
          nvidia-driver = config.hardware.nvidia.package;
        in
        (lib.mkMerge [
          [
            {
              hostPath = pkgs.addDriverRunpath.driverLink;
              containerPath = pkgs.addDriverRunpath.driverLink;
            }
            {
              hostPath = "${lib.getLib nvidia-driver}/etc";
              containerPath = "${lib.getLib nvidia-driver}/etc";
            }
            {
              hostPath = "${lib.getLib nvidia-driver}/share";
              containerPath = "${lib.getLib nvidia-driver}/share";
            }
            {
              hostPath = "${lib.getLib pkgs.glibc}/lib";
              containerPath = "${lib.getLib pkgs.glibc}/lib";
            }
            {
              hostPath = "${lib.getLib pkgs.glibc}/lib64";
              containerPath = "${lib.getLib pkgs.glibc}/lib64";
            }
          ]
          (lib.mkIf config.hardware.nvidia-container-toolkit.mount-nvidia-executables [
            {
              hostPath = lib.getExe' nvidia-driver "nvidia-cuda-mps-control";
              containerPath = "/usr/bin/nvidia-cuda-mps-control";
            }
            {
              hostPath = lib.getExe' nvidia-driver "nvidia-cuda-mps-server";
              containerPath = "/usr/bin/nvidia-cuda-mps-server";
            }
            {
              hostPath = lib.getExe' nvidia-driver "nvidia-debugdump";
              containerPath = "/usr/bin/nvidia-debugdump";
            }
            {
              hostPath = lib.getExe' nvidia-driver "nvidia-powerd";
              containerPath = "/usr/bin/nvidia-powerd";
            }
            {
              hostPath = lib.getExe' nvidia-driver "nvidia-smi";
              containerPath = "/usr/bin/nvidia-smi";
            }
          ])
          # nvidia-docker 1.0 uses /usr/local/nvidia/lib{,64}
          #   e.g.
          #     - https://gitlab.com/nvidia/container-images/cuda/-/blob/e3ff10eab3a1424fe394899df0e0f8ca5a410f0f/dist/12.3.1/ubi9/base/Dockerfile#L44
          #     - https://github.com/NVIDIA/nvidia-docker/blob/01d2c9436620d7dde4672e414698afe6da4a282f/src/nvidia/volumes.go#L104-L173
          (lib.mkIf config.hardware.nvidia-container-toolkit.mount-nvidia-docker-1-directories [
            {
              hostPath = "${lib.getLib nvidia-driver}/lib";
              containerPath = "/usr/local/nvidia/lib";
            }
            {
              hostPath = "${lib.getLib nvidia-driver}/lib";
              containerPath = "/usr/local/nvidia/lib64";
            }
          ])
        ]);
    };

    systemd.services.nvidia-container-toolkit-cdi-generator = {
      description = "Container Device Interface (CDI) for Nvidia generator";
      wantedBy = [ "multi-user.target" ];
      after = [ "systemd-udev-settle.service" ];
      serviceConfig = {
        RuntimeDirectory = "cdi";
        RemainAfterExit = true;
        ExecStart =
          let
            script = pkgs.callPackage ./cdi-generate.nix {
              inherit (config.hardware.nvidia-container-toolkit) mounts;
              nvidia-container-toolkit = config.hardware.nvidia-container-toolkit.package;
              nvidia-driver = config.hardware.nvidia.package;
              deviceNameStrategy = config.hardware.nvidia-container-toolkit.device-name-strategy;
            };
          in
          lib.getExe script;
        Type = "oneshot";
      };
    };

  };

}
