{
  config,
  lib,
  options,
  pkgs,
  ...
}:
let
  browserName =
    if (builtins.isString config.mods.homePackages.browser) then
      config.mods.homePackages.browser
    else if
      config.mods.homePackages.browser ? meta && config.mods.homePackages.browser.meta ? mainProgram
    then
      config.mods.homePackages.browser.meta.mainProgram
    else
      config.mods.homePackages.browser.pname;
in
{
  options.mods = {
    hyprland = {
      enable = lib.mkOption {
        default = true;
        example = false;
        type = lib.types.bool;
        description = ''
          Enable Hyprland
        '';
      };
      monitor = lib.mkOption {
        default = [
          # main monitor
          "${config.conf.defaultMonitor},${config.conf.defaultMonitorMode},0x0,${config.conf.defaultMonitorScale}"
          # all others
          ",highrr,auto,1"
        ];
        example = [ "DP-1,3440x1440@180,2560x0,1,vrr,0" ];
        type = with lib.types; listOf str;
        description = ''
          The monitor configuration for hyprland.
        '';
      };
      workspace = lib.mkOption {
        default = [ ];
        example = [ "2,monitor:DP-1, default:true" ];
        type = with lib.types; listOf str;
        description = ''
          The workspace configuration for hyprland.
        '';
      };
      noAtomic = lib.mkOption {
        default = false;
        example = true;
        type = lib.types.bool;
        description = ''
          Use tearing
        '';
      };
      extraAutostart = lib.mkOption {
        default = [ ];
        example = [ "your application" ];
        type = lib.types.listOf lib.types.str;
        description = ''
          Extra exec_once.
        '';
      };
      useDefaultConfig = lib.mkOption {
        default = true;
        example = false;
        type = lib.types.bool;
        description = ''
          Use preconfigured Hyprland config.
        '';
      };
      customConfig = lib.mkOption {
        default = { };
        example = { };
        type = with lib.types; attrsOf anything;
        description = ''
          Custom Hyprland configuration.
          Will be merged with default configuration if enabled.
        '';
      };
      plugins = lib.mkOption {
        default = [ ];
        example = [ ];
        type = with lib.types; listOf package;
        description = ''
          Plugins to be added to Hyprland.
        '';
      };
    };
  };

  config = lib.mkIf config.mods.hyprland.enable (
    lib.optionalAttrs (options ? wayland.windowManager.hyprland) {
      # install Hyprland related packages
      home.packages = with pkgs; [
        xorg.xprop
        grim
        slurp
        satty
        xdg-desktop-portal-gtk
        # xdg-desktop-portal-hyprland
        copyq
        wl-clipboard
        hyprcursor
        hyprpicker
      ];

      wayland.windowManager.hyprland = {
        enable = true;
        settings =
          if config.mods.hyprland.useDefaultConfig then
            {
              "$mod" = "SUPER";

              bindm = [
                "$mod, mouse:272, movewindow"
                "$mod, mouse:273, resizeactive"
              ];

              bind = [
                # screenshots
                ''$mod SUPER,S,exec,grim -g "$(slurp)" - | wl-copy''
                ''$mod SUPERSHIFT,S,exec,grim -g "$(slurp)" - | satty -f -''
                ''$mod SUPERSHIFTALT,S,exec,grim -c -g "2560,0 3440x1440" - | wl-copy''

                # regular programs
                "$mod SUPER,F,exec,${browserName}"
                (lib.mkIf (
                  browserName == "firefox" || browserName == "zen"
                ) "$mod SUPERSHIFT,F,exec,${browserName} -p special")
                "$mod SUPER,T,exec,kitty -1"
                "$mod SUPER,E,exec,nautilus -w"
                "$mod SUPER,N,exec,neovide"
                "$mod SUPER,M,exec,oxidash"
                "$mod SUPER,R,exec,anyrun"
                "$mod SUPER,G,exec,oxicalc"
                "$mod SUPER,D,exec,oxishut"
                "$mod SUPER,A,exec,oxipaste-iced"
                "$mod SUPERSHIFT,P,exec,hyprdock --gui"
                "$mod SUPERSHIFT,L,exec, playerctl -a pause & hyprlock & systemctl suspend"
                "$mod SUPERSHIFT,K,exec, playerctl -a pause & hyprlock & systemctl hibernate"

                # media keys
                (lib.mkIf config.mods.scripts.audioControl ",XF86AudioMute,exec, audioControl mute")
                (lib.mkIf config.mods.scripts.audioControl ",XF86AudioLowerVolume,exec, audioControl sink -5%")
                (lib.mkIf config.mods.scripts.audioControl ",XF86AudioRaiseVolume,exec, audioControl sink +5%")
                ",XF86AudioPlay,exec, playerctl play-pause"
                ",XF86AudioNext,exec, playerctl next"
                ",XF86AudioPrev,exec, playerctl previous"
                (lib.mkIf config.mods.scripts.changeBrightness ",XF86MonBrightnessDown,exec, change-brightness brightness 10%-")
                (lib.mkIf config.mods.scripts.changeBrightness ",XF86MonBrightnessUp,exec, change-brightness brightness +10%")

                # hyprland keybinds
                # misc
                "$mod SUPER,V,togglefloating,"
                "$mod SUPER,B,fullscreen,"
                "$mod SUPER,C,togglesplit"
                "$mod SUPER,Q,killactive,"
                "$mod SUPERSHIFTALT,M,exit,"
                "$mod SUPERSHIFT,W,togglespecialworkspace"

                # move
                "$mod SUPER,left,movewindow,l"
                "$mod SUPER,right,movewindow,r"
                "$mod SUPER,up,movewindow,u"
                "$mod SUPER,down,movewindow,d"

                # workspaces
                "$mod SUPER,1,workspace,1"
                "$mod SUPER,2,workspace,2"
                "$mod SUPER,3,workspace,3"
                "$mod SUPER,4,workspace,4"
                "$mod SUPER,5,workspace,5"
                "$mod SUPER,6,workspace,6"
                "$mod SUPER,7,workspace,7"
                "$mod SUPER,8,workspace,8"
                "$mod SUPER,9,workspace,9"
                "$mod SUPER,0,workspace,10"

                # move to workspace
                "$mod SUPERSHIFT,1,movetoworkspace,1"
                "$mod SUPERSHIFT,2,movetoworkspace,2"
                "$mod SUPERSHIFT,3,movetoworkspace,3"
                "$mod SUPERSHIFT,4,movetoworkspace,4"
                "$mod SUPERSHIFT,5,movetoworkspace,5"
                "$mod SUPERSHIFT,6,movetoworkspace,6"
                "$mod SUPERSHIFT,7,movetoworkspace,7"
                "$mod SUPERSHIFT,8,movetoworkspace,8"
                "$mod SUPERSHIFT,9,movetoworkspace,9"
                "$mod SUPERSHIFT,0,movetoworkspace,10"

                # move to workspace silent
                "$mod SUPERSHIFTALT,1,movetoworkspacesilent,1"
                "$mod SUPERSHIFTALT,2,movetoworkspacesilent,2"
                "$mod SUPERSHIFTALT,3,movetoworkspacesilent,3"
                "$mod SUPERSHIFTALT,4,movetoworkspacesilent,4"
                "$mod SUPERSHIFTALT,5,movetoworkspacesilent,5"
                "$mod SUPERSHIFTALT,6,movetoworkspacesilent,6"
                "$mod SUPERSHIFTALT,7,movetoworkspacesilent,7"
                "$mod SUPERSHIFTALT,8,movetoworkspacesilent,8"
                "$mod SUPERSHIFTALT,9,movetoworkspacesilent,9"
                "$mod SUPERSHIFTALT,0,movetoworkspacesilent,10"

                # preselection
                "$mod SUPERALT,j,layoutmsg,preselect l"
                "$mod SUPERALT,k,layoutmsg,preselect d"
                "$mod SUPERALT,l,layoutmsg,preselect u"
                "$mod SUPERALT,semicolon,layoutmsg,preselect r"
                "$mod SUPERALT,h,layoutmsg,preselect n"
              ];

              binde = [
                # hyprland keybinds
                # focus
                "$mod SUPER,J,movefocus,l"
                "$mod SUPER,semicolon,movefocus,r"
                "$mod SUPER,L,movefocus,u"
                "$mod SUPER,K,movefocus,d"

                # resize
                "$mod SUPER,U,resizeactive,-20 0"
                "$mod SUPER,P,resizeactive,20 0"
                "$mod SUPER,O,resizeactive,0 -20"
                "$mod SUPER,I,resizeactive,0 20"
              ];

              general = {
                gaps_out = "3,5,5,5";
                border_size = 3;
                "col.active_border" = lib.mkOverride 51 "0xFFFF0000 0xFF00FF00 0xFF0000FF 45deg";
                # "col.inactive_border" = "0x66333333";
                allow_tearing = lib.mkIf config.mods.hyprland.noAtomic true;
              };

              decoration = {
                rounding = 4;
              };

              animations = {
                bezier = "penguin,0.05,0.9,0.1,1.0";
                animation = [
                  "windowsMove,1,4,default"
                  "windows,1,7,default,popin 70%"
                  "windowsOut,1,7,default,popin 70%"
                  "border,1,10,default"
                  "fade,1,7,default"
                  "workspaces,1,6,default"
                  "layers,1,3,default,popin"
                ];
              };

              dwindle = {
                preserve_split = true;
                pseudotile = 0;
                permanent_direction_override = false;
              };

              input = {
                kb_layout = "${config.mods.xkb.layout}";
                kb_variant = "${config.mods.xkb.variant}";
                repeat_delay = 200;
                force_no_accel = true;
                touchpad = {
                  natural_scroll = true;
                  tap-to-click = true;
                  tap-and-drag = true;
                };
              };

              misc = {
                animate_manual_resizes = 1;
                enable_swallow = true;
                disable_splash_rendering = true;
                disable_hyprland_logo = true;
                swallow_regex = "^(.*)(kitty)(.*)$";
                initial_workspace_tracking = 1;
              };

              cursor = {
                # conversion seems to be borked right now, i want a smooth bibata :(
                enable_hyprcursor = false;
                no_hardware_cursors = lib.mkIf config.mods.gpu.nvidia.enable true;
                # no_break_fs_vrr = true;
              };

              gestures = {
                workspace_swipe = true;
              };

              monitor = config.mods.hyprland.monitor;
              workspace = config.mods.hyprland.workspace;

              env = [
                "GTK_CSD,0"
                ''TERM,"kitty /bin/fish"''
                "XDG_CURRENT_DESKTOP=Hyprland"
                "XDG_SESSION_TYPE=wayland"
                "XDG_SESSION_DESKTOP=Hyprland"
                "HYPRCURSOR_THEME,${config.mods.stylix.cursor.name}"
                "HYPRCURSOR_SIZE,${toString config.mods.stylix.cursor.size}"
                "XCURSOR_THEME,${config.mods.stylix.cursor.name}"
                "XCURSOR_SIZE,${toString config.mods.stylix.cursor.size}"
                "QT_QPA_PLATFORM,wayland"
                "QT_QPA_PLATFORMTHEME,qt5ct"
                "QT_WAYLAND_FORCE_DPI,96"
                "QT_AUTO_SCREEN_SCALE_FACTOR,0"
                "QT_WAYLAND_DISABLE_WINDOWDECORATION,1"
                "QT_SCALE_FACTOR,1"
                ''EDITOR,"neovide --novsync --nofork"''
                (lib.mkIf config.mods.hyprland.noAtomic "WLR_DRM_NO_ATOMIC,1")
                "GTK_USE_PORTAL, 1"

                (lib.mkIf config.mods.gpu.nvidia.enable "LIBVA_DRIVER_NAME,nvidia")
                (lib.mkIf config.mods.gpu.nvidia.enable "XDG_SESSION_TYPE,wayland")
                (lib.mkIf config.mods.gpu.nvidia.enable "GBM_BACKEND,nvidia-drm")
                (lib.mkIf config.mods.gpu.nvidia.enable "__GLX_VENDOR_LIBRARY_NAME,nvidia")
              ];

              layerrule = [
                # layer rules
                # mainly to disable animations within slurp and grim
                "noanim, selection"
              ];

              windowrule = [
                # window rules
                "tile,^(.*)(Spotify)(.*)$"
                "float,^(.*)(OxiCalc)(.*)$"
                "float,^(.*)(winecfg.exe)(.*)$"
                "float,^(.*)(speed.exe)(.*)$"
                "float,^(.*)(copyq)(.*)$"
                "center,^(.*)(swappy)(.*)$"
                "float,title:^(.*)(Spirit)(.*)$"
                "float,title:^(.*)(reset)(.*)$"
                "workspace 10 silent,^(.*)(steam)(.*)$"
                "workspace 9 silent,^(.*)(dota)(.*)$"
                "workspace 9 silent,^(.*)(battlebits)(.*)$"
                "workspace 9 silent,^(.*)(aoe)(.*)$"
                "suppressevent fullscreen maximize,^(.*)(neovide)(.*)$"
              ];

              windowrulev2 = [
                "immediate,class:^(.*)(Pal)$"
                "immediate,class:^(.*)(dota2)$"
                "immediate,class:^(.*)(needforspeedheat.exe)$"
              ];

              exec-once = [
                # environment
                "systemctl --user import-environment"
                "dbus-update-activation-environment --systemd --all"
                "hyprctl setcursor Bibata-Modern-Classic 24"

                # other programs
                "hyprpaper"
                "ironbar"
                "${browserName}"
                "oxipaste_daemon"
                "oxinoti"
              ] ++ config.mods.hyprland.extraAutostart;

              # plugin = {
              #   hyprspace = {
              #     bind = [
              #       "SUPER, W, overview:toggle, toggle"
              #     ];
              #   };
              # };
            }
            // config.mods.hyprland.customConfig
          else
            lib.mkForce config.mods.hyprland.customConfig;
        plugins = config.mods.hyprland.plugins;
        #inputs.Hyprspace.packages.${pkgs.system}.Hyprspace
      };
    }
  );
}
