{ lib, hmFlakes, pkgsFlakes, inputs, pkgs', config, ... }:
let
  inherit (builtins) head tail mapAttrs attrValues;
  inherit (lib) flatten optional optionalAttrs mkOption mkDefault mkOptionType mkIf;
  inherit (lib.my) homeStateVersion' homeStateVersion mkOpt' commonOpts inlineModule' applyAssertions;

  cfg = config.home-manager;

  mkHome = {
    config',
    defs,
  }:
  # homeManagerConfiguration doesn't allow us to set lib directly (inherits from passed pkgs)
  hmFlakes.${config'.home-manager}.lib.homeManagerConfiguration {
    # Passing pkgs here doesn't set the global pkgs, just where it'll be imported from (and where the global lib is
    # derived from). We want home-manager to import pkgs itself so it'll apply config and overlays modularly. Any config
    # and overlays previously applied will be passed on by `homeManagerConfiguration` though. In fact, because of weird
    # config merging behaviour (or lack thereof; similar to NixOS module), we explicitly pass empty config.
    # TODO: Check if this is fixed in future.
    pkgs = pkgs'.${config'.nixpkgs}.${config'.system} // { config = { }; };
    extraSpecialArgs = { inherit inputs pkgsFlakes; pkgsFlake = pkgsFlakes.${config'.nixpkgs}; };
    modules = (attrValues cfg.modules) ++ [
      {
        warnings = flatten [
          (optional (config'.nixpkgs != config'.home-manager)
          ''
            Using nixpkgs ${config'.nixpkgs} with home-manager ${config'.home-manager} may cause issues.
          '')
        ];

        _module.args = {
          pkgs' = mapAttrs (_: p: p.${config'.system}) pkgs';
        };

        home = {
          inherit (config') homeDirectory username;
        };
      }
      (homeStateVersion config'.home-manager)
    ] ++ defs;
  };

  homeOpts = with lib.types; { ... }@args:
  let
    config' = args.config;
  in
  {
    options = {
      inherit (commonOpts) system nixpkgs home-manager;
      # TODO: docCustom for home-manager?
      # Not possible I think, the doc generation code only ever includes its own modules...
      # (https://github.com/nix-community/home-manager/blob/0232fe1b75e6d7864fd82b5c72f6646f87838fc3/docs/default.nix#L37)
      homeDirectory = mkOpt' str null "Absolute path to home directory.";
      username = mkOpt' str null "Username for the configuration.";

      configuration = mkOption {
        description = "home-manager configuration module.";
        type = mkOptionType {
          name = "home-manager configuration";
          merge = _: defs: applyAssertions config (mkHome {
            inherit config';
            defs = map (d: inlineModule' d.file d.value) defs;
          });
        };
      };
    };

    config = {
      nixpkgs = mkDefault config'.home-manager;
    };
  };
in
{
  imports = [ modules/_list.nix ];
  options = with lib.types; {
    home-manager = {
      modules = mkOpt' (attrsOf commonOpts.moduleType) { } "home-manager modules to be exported by nixfiles.";
      homes = mkOpt' (attrsOf (submodule homeOpts)) { } "home-manager configurations to be exported by nixfiles.";
    };
  };
}
