# Downstream sugar: one call wires a whole machine flake.
# Users own ONLY system.nix, home.nix and theme-state.json; their flake.nix
# is generated once (by `nix flake init -t` or the future installer) and
# never hand-edited. The raw exports (nixosModules/homeModules/overlays)
# in flake.nix remain the escape hatch for power users.
{ nixpkgs, home-manager, nixos-hardware, nomarchy }:

let
  inherit (nixpkgs) lib;

  # Shared pure reader (modules/theme-state-read.nix) — re-exported so
  # power users composing without mkFlake can call it too.
  readThemeState = import ./modules/theme-state-read.nix { inherit lib; };
in
{
  inherit readThemeState;

  mkFlake =
    { src                      # the downstream flake directory (./.)
    , username                 # login name; also the homeConfigurations attr
    , hardwareProfile ? null   # nixos-hardware module name(s): string, list, or null
    , system ? "x86_64-linux"
    }:
    let
      # One profile or several (the installer's autodetection emits a few
      # common-* modules alongside the model-specific one).
      profileNames =
        if hardwareProfile == null then [ ]
        else if builtins.isList hardwareProfile then hardwareProfile
        else [ hardwareProfile ];

      lookupProfile = name:
        if builtins.hasAttr name nixos-hardware.nixosModules then
          nixos-hardware.nixosModules.${name}
        else
          throw ''
            nomarchy.lib.mkFlake: unknown hardwareProfile "${name}".
            ${let
                close = builtins.filter
                  (candidate: lib.strings.levenshteinAtMost 3 candidate name)
                  (builtins.attrNames nixos-hardware.nixosModules);
              in
              lib.optionalString (close != [ ])
                "Did you mean: ${lib.concatStringsSep ", " close}?\n"
            }Valid names are the attribute names of nixos-hardware.nixosModules:
              https://github.com/NixOS/nixos-hardware
            or list them locally with:
              nix eval github:NixOS/nixos-hardware#nixosModules --apply builtins.attrNames
            Omit hardwareProfile entirely if your machine has no profile.
          '';

      hardwareModules = map lookupProfile profileNames;

      pkgs = import nixpkgs {
        inherit system;
        overlays = [ nomarchy.overlays.default ];
        # Matches nixpkgs.config.allowUnfree in modules/nixos: the system
        # and the standalone HM desktop see the same package set.
        config.allowUnfree = true;
      };

      # Early fail-closed gate: missing/empty/non-object theme-state.json
      # throws here with a template + validate pointer, before module
      # evaluation buries a raw readFile/fromJSON stack. Field-level
      # schema still runs in modules/home/theme.nix after defaults merge.
      # Forced via seq on the whole return set — attrNames alone must not
      # skip the check (Nix is lazy on unused let bindings and attr values).
      statePath = src + "/theme-state.json";
      _themeState = readThemeState statePath;
    in
    builtins.seq _themeState {
      # System layer — rebuilt rarely:
      #   sudo nixos-rebuild switch --flake .#default
      nixosConfigurations.default = nixpkgs.lib.nixosSystem {
        inherit system;
        specialArgs = { inherit username; };
        modules =
          [
            nomarchy.nixosModules.nomarchy
            # The standalone HM CLI ships with the system so theme switching
            # (`nomarchy-theme-sync apply` → `home-manager switch`) works
            # out of the box — same pinned input as the desktop modules.
            { environment.systemPackages = [ home-manager.packages.${system}.home-manager ]; }
            # System-side theme consumers (Plymouth splash background)
            # read the same JSON the desktop does.
            { nomarchy.system.stateFile = statePath; }
          ]
          ++ hardwareModules
          ++ [
            (src + "/hardware-configuration.nix") # from nixos-generate-config
            (src + "/system.nix")
          ];
      };

      # Desktop layer — every theme change, no sudo:
      #   home-manager switch --flake .#<username>
      # (`nomarchy-theme-sync apply` runs this for you.)
      homeConfigurations.${username} = home-manager.lib.homeManagerConfiguration {
        inherit pkgs;
        modules = [
          nomarchy.homeModules.nomarchy
          (src + "/home.nix")
          {
            # Written by nomarchy-theme-sync; reading it is pure — the
            # file is part of the downstream flake's source.
            nomarchy.stateFile = statePath;
            home = {
              inherit username;
              homeDirectory = "/home/${username}";
            };
          }
        ];
      };
    };
}
