{ lib, pkgsFlake, pkgs, pkgs', inputs, config, ... }@args:
let
  inherit (builtins) listToAttrs mapAttrs readFile;
  inherit (lib)
    optionalString nameValuePair concatMapStrings concatStringsSep optionalAttrs versionAtLeast
    mapAttrsToList mkMerge mkIf mkDefault mkOption;
  inherit (lib.hm) dag;
  inherit (lib.my) mkOpt' dummyOption;
in
{
  options = with lib.types; {
    my = {
      isStandalone = mkOption {
        type = bool;
        internal = true;
        description = "Whether home-manager is running inside a NixOS system or not.";
      };

      shell = mkOpt' str null "User's shell (so NixOS or others can set it externally).";
      fishCompletionsFrequency = mkOpt' (nullOr str) "daily" "How often to generate fish completions from manpages.";

      ssh = {
        authKeys = {
          literal = mkOpt' (listOf singleLineStr) [ ] "List of OpenSSH keys to allow";
          files = mkOpt' (listOf path) [ ] "List of OpenSSH key files to allow";
        };
      };
    };
  };
  config = mkMerge [
    {
      my = {
        isStandalone = !(args ? osConfig);

        shell = mkDefault "${config.programs.fish.package}/bin/fish";
      };

      home = {
        file.".ssh/authorized_keys" = with config.my.ssh.authKeys;
          mkIf (config.programs.ssh.enable && (literal != [ ] || files != [ ])) {
            text = ''
              ${concatStringsSep "\n" literal}
              ${concatMapStrings (f: readFile f + "\n") files}
            '';
          };
      };

      nix = {
        package = mkIf (!(versionAtLeast config.home.stateVersion "22.11")) pkgs.nix;
        settings = with lib.my.c.nix; {
          experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
          max-jobs = mkDefault "auto";

          extra-substituters = cache.substituters;
          extra-trusted-public-keys = cache.keys;
          connect-timeout = 5;
          fallback = true;
        };
      };

      programs = {
        # Even when enabled this will only be actually installed in standalone mode
        # Note: `home-manager.path` is for telling home-manager is installed and setting it in NIX_PATH, which we should
        # never care about.
        home-manager.enable = true;

        lsd = {
          enable = mkDefault true;
          enableFishIntegration = mkDefault true;
        };

        starship = {
          enable = mkDefault true;
          settings = {
            aws.disabled = true;
          };
        }
        # We use custom behaviour for this
        // listToAttrs (map (s: nameValuePair "enable${s}Integration" false) [ "Bash" "Zsh" "Fish" ]);

        tmux = {
          enable = true;
          keyMode = "vi";
        };

        bash = {
          # This does not install bash but has home-manager control .bashrc and friends
          # Bash has some really weird behaviour with non-login and non-interactive shells, particularly around which
          # of profile and bashrc are loaded when. This causes issues with PATH not being set correctly for
          # non-interactive SSH...
          enable = mkDefault true;
          initExtra =
          ''
            flake-src() {
              cd "$(nix eval "''${@:2}" --impure --raw --expr "builtins.getFlake \"$1\"")"
            }
          '';
          shellAliases = {
            hm = "home-manager";
          };
        };

        fish = {
          enable = mkDefault true;
          interactiveShellInit =
            # TODO: Pull request?
            (optionalString config.programs.starship.enable
            ''
              # Adapted from https://github.com/nix-community/home-manager/blob/0232fe1b75e6d7864fd82b5c72f6646f87838fc3/modules/programs/starship.nix#L113
              # linux is the VTTY, which doesn't seem to have a suitable font for starship
              if test "$TERM" != "dumb" -a "$TERM" != "linux" -a \( -z "$INSIDE_EMACS"  -o "$INSIDE_EMACS" = "vterm" \)
                eval (${config.home.profileDirectory}/bin/starship init fish)
              end
            '');
          functions = {
            # Silence the default greeting
            fish_greeting = ":";
            flake-src = {
              description = "cd into a flake reference's source directory";
              body = ''cd (nix eval $argv[2..] --impure --raw --expr "builtins.getFlake \"$argv[1]\"")'';
            };
          };
          shellAbbrs = {
            hm = "home-manager";
            k = "kubectl";
          };
          shellAliases = {
            ip = "ip --color=auto";
            s = "kitty +kitten ssh";
          };
        };

        ssh = {
          enable = mkDefault true;
          enableDefaultConfig = false;
          settings = {
            nix-dev-vm = {
              User = "dev";
              HostName = "localhost";
              Port = 2222;
              StrictHostKeyChecking = "no";
              UserKnownHostsFile = "/dev/null";
            };

            "rsync.net" = {
              Host = "rsyncnet";
              User = "16413";
              HostName = "ch-s010.rsync";
            };

            shoe = {
              Host = "shoe.netsoc.tcd.ie shoe";
              User = "netsoc";
            };
            netsocBoxes = {
              Host = "cube spoon napalm gandalf saruman";
              User = "root";
            };

            "*" = {
              IdentityFile = [
                "~/.ssh/id_rsa"
                "~/.ssh/borg"
              ];
            };
          };
        };

        direnv = {
          enable = mkDefault true;
          nix-direnv.enable = true;
          stdlib =
            ''
              # addition to nix-direnv's use_nix that registers outputs as gc roots (as well as the .drv)
              use_nix_outputs() {
                local layout_dir drv deps
                layout_dir="$(direnv_layout_dir)"
                drv="$layout_dir/drv"
                deps="$layout_dir/deps"

                if [ ! -e "$deps" ] || (( "$(stat --format=%Z "$drv")" > "$(stat --format=%Z "$deps")" )); then
                  rm -rf "$deps"
                  mkdir -p "$deps"
                  nix-store --indirect --add-root "$deps/out" --realise $(nix-store --query --references "$drv") > /dev/null
                  log_status renewed outputs gc roots
                fi
              }
            '';
        };

        htop = {
          enable = true;
          settings = {};
        };
      };

      home = {
        packages = with pkgs; [
          file
          tree
          pwgen

          minicom
          iperf3
          mosh
          wget
          whois
          ldns
          mtr

          hyx
          ncdu
          jq
          yq-go
          nix-tree
        ];

        sessionVariables = {
          EDITOR = "vim";
        };

        language.base = mkDefault "en_IE.UTF-8";
      };
    }
    (mkIf (config.my.isStandalone || !args.osConfig.home-manager.useGlobalPkgs) {
      # Note: If globalPkgs mode is on, then these will be overridden by the NixOS equivalents of these options
      nixpkgs = {
        overlays = [
          inputs.deploy-rs.overlays.default
          inputs.boardie.overlays.default
          inputs.nixGL.overlays.default
        ];
        config = {
          allowUnfree = true;
        };
      };
      nix = {
        registry = {
          pkgs = {
            to = {
              type = "path";
              path = "${pkgsFlake}";
            };
            exact = true;
          };
        };
      };
    })
    (mkIf config.my.isStandalone {
      my = {
        ssh.authKeys.files = [ lib.my.c.sshKeyFiles.me ];
      };

      nix.package = mkIf (versionAtLeast config.home.stateVersion "22.05") pkgs.nix;

      fonts.fontconfig.enable = true;

      home = {
        packages = with pkgs; [
          pkgs'.mine.nix
        ];

        # Without this, we are at the mercy of whatever version of nix is in $PATH...
        # TODO: Is this the right thing to do?
        extraActivationPath = [
          config.nix.package
        ];
      };
    })
    (mkIf pkgs.stdenv.isLinux (mkMerge [
      {
        home = {
          packages = with pkgs; [
            iputils
            traceroute
          ];
        };
      }
      (mkIf (config.my.isStandalone && config.programs.fish.enable && config.my.fishCompletionsFrequency != null) {
        systemd.user = {
          services.fish-update-completions = {
            Unit.Description = "fish completions update";

            Service = {
              Type = "oneshot";
              ExecStart = "${config.programs.fish.package}/bin/fish -c fish_update_completions";
            };
          };

          timers.fish-update-completions = {
            Unit.Description = "fish completions update timer";

            Timer = {
              OnCalendar = config.my.fishCompletionsFrequency;
              Persistent = true;
              Unit = "fish-update-completions.service";
            };

            Install.WantedBy = [ "timers.target" ];
          };
        };
      })
    ]))
    (mkIf (pkgs.stdenv.isDarwin && config.my.isStandalone) {
      home = {
        # No targets.genericLinux equivalent apparently
        sessionVariablesExtra =
          ''
            . "${config.nix.package}/etc/profile.d/nix.sh"
          '';
        packages = with pkgs; [
          cacert
        ];
      };
    })
  ];
}
