{lib}: let
  inherit (lib.self.capacitor.compose) combineSubtrees;

  # Make an overlay for the definitions of the children.
  #
  # The overlay can not encompass the parent flake since the peers of the
  # flake are unknown.
  #
  # NOTE: Although similar to the definition of `subtrees` this function
  # only extracts *children's definitions* not their already combined subtrees
  overlay = {
    children,
    prunedSubtrees,
    ...
  }:
    lib.pipe (lib.attrValues children) [
      # extract children's own definitions
      (lib.catAttrs "self")
      # Concatenate children's definitions.
      #
      # In this step multiple definitions of the same attribute are possible
      # and may replace each other in arbitrary order.
      # The set is therefore referred to as "unsafe"
      (lib.foldl' (prev: child: prev // child) {})
      # Replace the conflicting attributes with error notices.
      # These can only be generated by the _importing_ (parent) flake
      # as the children are not aware of each other
      #
      # As a result of this a "safe" attrset of packages is produced
      (unsafeCombinedChildren: unsafeCombinedChildren // (prunedSubtrees.conflicts))
      # The safe attrset is then wrapped as an overlay
      (safeCombinedChildren: makeOverlay lib prunedSubtrees.duplicates prunedSubtrees.candidates (lib.attrValues safeCombinedChildren))
    ];

  collectOverlays = lib: root: let
    collect = parent:
      (lib.flatten (lib.mapAttrsToList (_: collect) parent.children)) ++ [(overlay parent)];
  in
    collect {
      children.root = root;
      prunedSubtrees = {
        conflicts = {};
        duplicates = {};
        candidates = {};
      };
      overlay = _: _: {};
    };

  # Overlay creation process:
  # 1. a list of blueprint closures is materialized to an attribute set
  # 2. for each closure determine the package set it is called with by combining
  #    all (sub)packagesets in its scope
  # 3. return a call to the closures' function with the scoped package set and context
  makeOverlay = lib: duplicates: candidates: base: (
    final: prev: let
      localOverlay =
        lib.capacitor.capacitate.materialize
        (
          {
            namespace,
            fn,
            ...
          } @ closure: let
            parentNamespaceDelta =
              if lib.last namespace != "__functor"
              then 1
              else 2;
            scopeNamespace = lib.take (lib.length namespace - parentNamespaceDelta) namespace;

            # final' = final;
            #  lib.setAttrByPath
            #   (lib.init namespace)
            #   ((
            #     builtins.removeAttrs
            #       (lib.getAttrFromPath (lib.init namespace) final)
            #       [(lib.last namespace)]
            #     ) // lib.optionalAttrs
            #       (lib.hasAttrByPath namespace prev)
            #       {${lib.last namespace} = lib.getAttrFromPath namespace prev;});

            # TODO: use actual nixpkgs scopes (makeScope) here?
            scope =
              lib.foldl'
              (acc: packageSetAttributeName: acc // (acc.${packageSetAttributeName} or {}))
              final
              scopeNamespace;

            callPackageWith = auto: callee: extra: let
              autoAttributes =
                scope
                // closure.context
                // (closure.context.context' prev.system)
                // {
                  context = closure.context;
                }
                // {
                  closure = closure;
                  callPackageWith = callPackageWith;
                  callPackage = callPackageWith {};
                }
                // lib.setAttrByPath namespace (lib.attrByPath namespace (throw "Missing base definition of ${lib.showAttrPath namespace}") prev)
                // {inherit candidates;}
                // {lib = closure.context.lib // final.lib;}
                // auto;
            in
              final.lib.callPackageWith
              autoAttributes
              callee
              extra;
          in {
            path = namespace;
            value = callPackageWith {} fn {};
          }
        )
        (builtins.filter (def: (!(duplicates ? ${def.attrName}) && !def.circular)) base);
    in
      # TODO: merge manually?
      # lib.mapAttrsRecursiveCond (a: ! lib.isFunction a) (
      #   namespace: merger:
      #     if lib.isFunction merger
      #     then merger (lib.attrByPath namespace {} prev)
      #     else merger
      # )
      lib.recursiveUpdateUntil (path: l: r: lib.isDerivation r) prev localOverlay
  );

  combinedSubtrees = system: context:
    combineSubtrees (context // {system = system;}) {};

  /*
  Return attribute set of packages from overlay
  Type: callOverlay :: (pkgs -> pkgs -> attrs) -> attrs
  Example:
    callOverlay (final: prev: { a = 42; })
    => { a = 42 }
  */
  callOverlay = pkgs: overlay: let
    newPkgs =
      overlay newPkgs pkgs
      // {
        pkgs = newPkgs;
        lib = pkgs.lib;
        callPackage = pkgs.lib.callPackageWith newPkgs;
      };
  in
    overlay newPkgs pkgs;

  /*
  Return attribute set of packages from overlays
  Type: callOverlays :: [pkgs -> pkgs -> attrs] -> attrs
  Example:
    callOverlays [(final: prev: { a = 42; }) (final: prev: { b = 123; })]
    => { a = 42; b = 123; }
  */
  callOverlays = pkgs: overlays:
    callOverlay pkgs (lib.composeManyExtensions overlays);
in {
  overlay = context: final: prev:
    (lib.composeManyExtensions (collectOverlays lib (combinedSubtrees prev.system context))) final prev;

  composedClean = context:
    lib.genAttrs context.systems (system:
      callOverlays context.nixpkgs.legacyPackages.${system} (collectOverlays lib (combinedSubtrees system context)));

  overlays = system: context: collectOverlays lib (combinedSubtrees system context);

  inherit combinedSubtrees;

  plugin = {context, ...}: let
    nixpkgs = context.nixpkgs;
  in {
    # combinedSubtrees = combinedSubtrees context.self;

    overlays.default = final: prev:
      (lib.composeManyExtensions (combinedSubtrees prev.system context.self)) final prev;

    composed = lib.genAttrs context.self.__reflect.systems (system:
      lib.callPackage nixpkgs {
        inherit system;
        overlays = collectOverlays context.lib (combinedSubtrees system context.self);
      });

    composedClean =
      lib.genAttrs context.self.__reflect.systems (system:
        callOverlays nixpkgs.legacyPackages.${system} (collectOverlays context.lib (combinedSubtrees system context.self)));

    # lib.allowRedefinition = (
    #   fn: {
    #     context,
    #     lib,
    #     pkgs,
    #   }: let
    #     value = context.callPackageWith context.pkgs fn {};
    #   in
    #     if (context.isBaseDefinition || context.circular)
    #     then value
    #     else lib.attrByPath context.namespace value context.prev
    # );

    # lib.allowRedefinition' = (
    #   context: value:
    #     if context.circular
    #     then value
    #     else lib.attrByPath context.namespace value context.pkgs
    # );
  };
}
