{
  inputs,
  lib,
  config,
  pkgs,
  ...
}:
let
  cfg = config.niriwm;
  unstable = import inputs.nixpkgs-unstable {
    system = "x86_64-linux";
    config.allowUnfree = true;
  };
in
{
  options.niriwm = {
    enable = lib.mkEnableOption "Enable niri window manager.";
    useNonFree = lib.mkOption {
      default = false;
      example = true;
      description = "Whether to enable non-free software in the niri config";
    };
    installGaming = lib.mkOption {
      default = false;
      example = true;
      description = "Whether to install gaming software on the system.";
    };
    systemPackages = lib.mkOption {
      default = [ ];
      description = "Add any additional packages desired. Merged with niri defaults.";
    };
    user = lib.mkOption {
      type = lib.types.str;
      description = "Primary user for niri-related service configs";
    };
  };

  config = lib.mkIf cfg.enable {

    nixpkgs.config.allowUnfree = cfg.useNonFree;

    # Portal config: programs.niri.enable adds xdg-desktop-portal-gnome
    # and niri-portals.conf via configPackages, but the portal daemon
    # doesn't reliably pick up configPackages files. Setting config
    # explicitly ensures ScreenCast routes to the GNOME portal.
    xdg.portal.config.niri = {
      default = [ "gnome" "gtk" ];
      "org.freedesktop.impl.portal.Access" = [ "gtk" ];
      "org.freedesktop.impl.portal.Notification" = [ "gtk" ];
      "org.freedesktop.impl.portal.Secret" = [ "gnome-keyring" ];
    };
    xdg.sounds.enable = true;

    ###
    ##  System Packages
    ###
    environment.systemPackages =
      with pkgs;
      lib.lists.flatten [
        [
          bash
          egl-wayland
          git
          glib # gsettings
          grim
          libnotify
          lxqt.lxqt-policykit
          nautilus
          slurp
          swaylock
          syncthingtray
          unstable.ghostty
          unstable.xwayland-satellite
          wl-clipboard
          xdg-utils
          zsh
        ]
        cfg.systemPackages
      ];
    environment.variables.QT_STYLE_OVERRIDE = "kvantum";
    environment.sessionVariables = {
      MOZ_ENABLE_WAYLAND = "1";
      # QT_QPA_PLATFORM and GDK_BACKEND must NOT be set - they break the screencast portal
      WLR_NO_HARDWARE_CURSORS = "1";
      ELECTRON_OZONE_PLATFORM_HINT = "auto";
      NIXOS_OZONE_WL = "1";
    };

    programs.gamemode = {
      enable = lib.mkDefault cfg.installGaming;
      settings = {
        general = {
          reaper_freq = 5;
          desiredgov = "performance";
          softrealtime = "auto";
        };
      };
    };
    programs.niri.enable = true;
    programs.regreet.enable = lib.mkDefault true;
    programs.xfconf.enable = true;
    programs.zsh.enable = true;
    programs.ssh.startAgent = false; # Using GNOME Keyring's gcr-ssh-agent instead
    programs.adb.enable = true;
    programs.steam.enable = lib.mkDefault cfg.installGaming;

    # For nautilus
    services.gnome.sushi.enable = true;
    programs.nautilus-open-any-terminal = {
      enable = true;
      terminal = "ghostty";
    };

    # Set zsh as the default shell system-wide
    users.defaultUserShell = pkgs.zsh;
    environment.shells = with pkgs; [
      zsh
      bash
    ];

    ###
    ##  Services
    ###
    services.gvfs.enable = true; # file manager mount, trash, etc
    services.tumbler.enable = true; # thumbnails
    services.openssh.enable = true;
    services.dbus.enable = true;
    services.gnome.gnome-keyring.enable = true;
    services.flatpak.enable = true;
    services.usbmuxd.enable = lib.mkDefault false;

    # For yubioath desktop
    services.pcscd.enable = lib.mkDefault true;

    # Printing
    services.printing = {
      enable = true;
      browsing = true;
      drivers = [ pkgs.brlaser ];
    };

    # Audio - PipeWire
    services.pulseaudio.enable = false;
    security.rtkit.enable = true;
    services.pipewire = {
      enable = true;
      audio.enable = true;
      alsa.enable = true;
      alsa.support32Bit = true;
      pulse.enable = true;
      wireplumber.enable = true;
      # Raise minimum buffer to reduce audio popping caused by USB bus
      # contention (e.g. webcam + audio interfaces sharing a controller)
      extraConfig.pipewire."10-clock-settings" = {
        "context.properties" = {
          "default.clock.min-quantum" = 512;
        };
      };
      wireplumber.extraConfig = {
        "wireplumber.settings" = {
          bluetooth.autoswitch-to-headset-profile = false;
        };
        bluetoothEnhancements = {
          "monitor.bluez.properties" = {
            "bluez5.enable-sbc-xq" = true;
            "bluez5.enable-msbc" = true;
            "bluez5.enable-hw-volume" = true;
            "bluez5.roles" = [
              "a2dp_sink"
              "a2dp_source"
              "bap_sink"
              "bap_source"
              "hfp_hf"
              "hfp_ag"
            ];
          };
        };
      };
    };

    ###
    ##  Misc
    ###
    security.polkit.enable = true;

    # Keyring setup
    security.pam.services.greetd.enableGnomeKeyring = true;
    security.pam.services.login.enableGnomeKeyring = true;

    hardware.bluetooth = {
      enable = lib.mkDefault true;
      powerOnBoot = lib.mkDefault true;
      settings = {
        General = {
          ControllerMode = lib.mkDefault "dual";
          FastConnectable = lib.mkDefault "true";
          Experimental = lib.mkDefault "true";
        };
        Policy = {
          AutoEnable = lib.mkDefault "true";
        };
        LE = {
          EnableAdvMonInterleaveScan = lib.mkDefault 1;
        };
      };
    };

    # Hardware scanning support
    hardware.sane = {
      enable = lib.mkDefault true;
      brscan5.enable = lib.mkDefault true;
    };

    # Man pages
    documentation.dev.enable = lib.mkDefault true;

    # Betaflight configurator udev rules (shared across hosts that use it)
    services.udev.extraRules = lib.mkDefault ''
      # DFU (Internal bootloader for STM32 and AT32 MCUs)
      SUBSYSTEM=="usb", ATTRS{idVendor}=="2e3c", ATTRS{idProduct}=="df11", MODE="0664", GROUP="dialout"
      SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0664", GROUP="dialout"
    '';
  };
}
