{
  lib,
  config,
  pkgs,
  ...
}:
{
  imports = [
    ../default.nix
    ../wlr
    ./swaylock.nix
    ./swayidle.nix
  ];

  home.packages = with pkgs; [
    grimblast
    pamixer
    playerctl
  ];

  wayland.windowManager.hyprland = {
    enable = true;
    systemd = {
      enable = true;
    };

    settings = {
      general = {
        gaps_in = 15;
        gaps_out = 20;
        border_size = 1;
      };
      cursor.inactive_timeout = 4;

      device = [
        {
          name = "royuan-akko-keyboard";
          kb_layout = "us_intl";
        }
      ];

      dwindle = {
        split_width_multiplier = 1.35;
        pseudotile = true;
      };

      layerrule = [
        "animation fade,hyprpicker"
        "animation fade,selection"
        "blur,^(wofi)$"
        "ignorezero,^(wofi)$"
      ];

      windowrulev2 =
        let
          steam = "title:^()$,class:^(steam)$";
        in
        [
          "stayfocused, ${steam}"
          "minsize 1 1, ${steam}"
        ];

      decoration = {
        active_opacity = 1.0;
        inactive_opacity = 0.85;
        fullscreen_opacity = 1.0;
        rounding = 7;
        blur = {
          enabled = true;
          size = 4;
          passes = 3;
          new_optimizations = true;
          ignore_opacity = true;
        };
        drop_shadow = true;
        shadow_range = 12;
        shadow_offset = "3 3";
      };

      animations = {
        enabled = true;
        bezier = [
          "easein,0.1, 0, 0.5, 0"
          "easeinback,0.35, 0, 0.95, -0.3"

          "easeout,0.5, 1, 0.9, 1"
          "easeoutback,0.35, 1.35, 0.65, 1"

          "easeinout,0.45, 0, 0.55, 1"
        ];

        animation = [
          "fadeIn,1,3,easeout"
          "fadeLayersIn,1,3,easeoutback"
          "layersIn,1,3,easeoutback,slide"
          "windowsIn,1,3,easeoutback,slide"

          "fadeLayersOut,1,3,easeinback"
          "fadeOut,1,3,easein"
          "layersOut,1,3,easeinback,slide"
          "windowsOut,1,3,easeinback,slide"

          "border,1,3,easeout"
          "fadeDim,1,3,easeinout"
          "fadeShadow,1,3,easeinout"
          "fadeSwitch,1,3,easeinout"
          "windowsMove,1,3,easeoutback"
          "workspaces,1,2.6,easeoutback,slide"
        ];
      };

      bindm = [
        "SUPER,mouse:272,movewindow"
        "SUPER,mouse:273,resizewindow"
      ];

      bind =
        let
          terminal = "${pkgs.alacritty}/bin/alacritty";
          grimblast = lib.getExe pkgs.grimblast;
          workspaces = [
            "1"
            "2"
            "3"
            "4"
            "5"
          ];
          directions = rec {
            left = "l";
            right = "r";
            up = "u";
            down = "d";
            h = left;
            l = right;
            k = up;
            j = down;
          };
        in
        [
          "SUPER,Return,exec,${terminal}"
          "SUPERSHIFT,q,killactive" # exit program
          "SUPERSHIFT,e,exit" # exit hyprland
          "SUPERSHIFT,r,exec,hyprctl reload" # reload currently running hyprland

          "SUPER,s,togglesplit" # horizontal split
          "SUPER,f,fullscreen,1" # borderless window
          "SUPERSHIFT,f,fullscreen,0" # proper fullscreen
          "ALTSHIFT,space,togglefloating" # floating window

          "SUPER,minus,splitratio,-0.25" # split gets slightly smaller
          "SUPERSHIFT,minus,splitratio,-0.3333333" # split gets smaller
          "SUPER,equal,splitratio,0.25" # split gets slightly larger
          "SUPERSHIFT,equal,splitratio,0.3333333" # split gets larger

          "SUPER,g,togglegroup" # make a window group
          "SUPER,t,lockactivegroup,toggle" # lock/unlock the current group
          "SUPER,tab,changegroupactive,f" # switch to next window in group
          "SUPERSHIFT,tab,changegroupactive,p" # switch to prev window in group

          "SUPER,dead_grave,workspace,previous" # prev workspace
          "SUPER,dead_grave,workspace,next" # next workspace

          "SUPER,u,togglespecialworkspace" # toggle special workspace
          "SUPERSHIFT,u,movetoworkspacesilent,special" # move to special workspace

          ",Print,exec,${grimblast} --notify --freeze copy area" # screenshot area
          "SHIFT,Print,exec,${grimblast} --notify --freeze copy output" # screenshot all

          ",XF86AudioRaiseVolume,exec,pamixer -i 5" # raise volume
          ",XF86AudioLowerVolume,exec,pamixer -d 5" # lower volume
          ",XF86AudioPlay,exec,playerctl play-pause" # play/pause
          ",XF86AudioNext,exec,playerctl next" # next song
          ",XF86AudioPrev,exec,playerctl previous" # previous song
        ]
        ++
          # change workspace
          (map (n: "SUPER,${n},workspace,name:${n}") workspaces)
        ++
          # move window to workspace
          (map (n: "SUPERSHIFT,${n},movetoworkspacesilent,name:${n}") workspaces)
        ++
          # move focus
          (lib.mapAttrsToList (key: direction: "SUPER,${key},movefocus,${direction}") directions)
        ++
          # swap windows
          (lib.mapAttrsToList (key: direction: "SUPERSHIFT,${key},swapwindow,${direction}") directions)
        ++
          # move windows
          (lib.mapAttrsToList (
            key: direction: "SUPERCONTROL,${key},movewindoworgroup,${direction}"
          ) directions)
        ++
          # move monitor focus
          (lib.mapAttrsToList (key: direction: "SUPERALT,${key},focusmonitor,${direction}") directions)
        ++
          # move workspace to other monitor
          (lib.mapAttrsToList (
            key: direction: "SUPERALTSHIFT,${key},movecurrentworkspacetomonitor,${direction}"
          ) directions)
        ++
          # launcher
          (
            let
              wofi = lib.getExe pkgs.wofi;
            in
            lib.optionals config.programs.wofi.enable [
              "SUPER,Space,exec,${wofi} -S drun -W 20% -H 15%"
              "SHIFTSUPER,Space,exec,${wofi} -S run -W 20% -H 15%"
            ]
          );

      monitor = map (
        m:
        "${m.name},${
          if m.enabled then
            "${toString m.width}x${toString m.height}@${toString m.refreshRate},${m.position},${m.scale}"
          else
            "disable"
        }"
      ) config.monitors;

      workspace = map (m: "name:${m.workspace},monitor:${m.name}") (
        lib.filter (m: m.enabled && m.workspace != null) config.monitors
      );
    };
  };
}