{
  config,
  lib,
  pkgs,
  ...
}:

let
  cfg = config.qt;

  qtctFormat = pkgs.formats.ini {
    listToValue = values: lib.concatStringsSep ", " values;
  };

  # Map platform names to their packages.
  platformPackages = with pkgs; {
    gnome = [
      qgnomeplatform
      qgnomeplatform-qt6
    ];
    adwaita = [
      qadwaitadecorations
      qadwaitadecorations-qt6
    ];
    gtk = [
      libsForQt5.qtstyleplugins
      qt6Packages.qt6gtk2
    ];
    kde = [
      kdePackages.kio
      kdePackages.plasma-integration
      kdePackages.systemsettings
    ];
    lxqt = [
      lxqt.lxqt-qtplugin
      lxqt.lxqt-config
    ];
    qtct = [
      libsForQt5.qt5ct
      qt6Packages.qt6ct
    ];
  };

  # Maps style names to their QT_QPA_PLATFORMTHEME, if necessary.
  styleNames = {
    gtk = "gtk2";
    qtct = "qt5ct";
  };

  # Maps known lowercase style names to style packages. Non-exhaustive.
  stylePackages = with pkgs; {
    bb10bright = libsForQt5.qtstyleplugins;
    bb10dark = libsForQt5.qtstyleplugins;
    cleanlooks = libsForQt5.qtstyleplugins;
    gtk2 = [
      libsForQt5.qtstyleplugins
      qt6Packages.qt6gtk2
    ];
    motif = libsForQt5.qtstyleplugins;
    cde = libsForQt5.qtstyleplugins;
    plastique = libsForQt5.qtstyleplugins;

    adwaita = [
      adwaita-qt
      adwaita-qt6
    ];
    adwaita-dark = [
      adwaita-qt
      adwaita-qt6
    ];
    adwaita-highcontrast = [
      adwaita-qt
      adwaita-qt6
    ];
    adwaita-highcontrastinverse = [
      adwaita-qt
      adwaita-qt6
    ];

    breeze = [
      kdePackages.breeze
      kdePackages.breeze.qt5
    ];

    kvantum = [
      libsForQt5.qtstyleplugin-kvantum
      qt6Packages.qtstyleplugin-kvantum
    ];
  };

in
{
  meta.maintainers = with lib.maintainers; [
    rycee
    thiagokokada
  ];

  imports = [
    (lib.mkChangedOptionModule [ "qt" "useGtkTheme" ] [ "qt" "platformTheme" ] (
      config: if lib.getAttrFromPath [ "qt" "useGtkTheme" ] config then "gtk" else null
    ))
  ];

  options = {
    qt = {
      enable = lib.mkEnableOption "Qt 5 and 6 configuration";

      platformTheme =
        let
          newOption = {
            name = lib.mkOption {
              type = with lib.types; nullOr str;
              default = null;
              example = "adwaita";
              relatedPackages = [
                "qgnomeplatform"
                "qgnomeplatform-qt6"
                "qadwaitadecorations"
                "qadwaitadecorations-qt6"
                [
                  "libsForQt5"
                  "plasma-integration"
                ]
                [
                  "libsForQt5"
                  "qt5ct"
                ]
                [
                  "libsForQt5"
                  "qtstyleplugins"
                ]
                [
                  "libsForQt5"
                  "systemsettings"
                ]
                [
                  "kdePackages"
                  "plasma-integration"
                ]
                [
                  "kdePackages"
                  "systemsettings"
                ]
                [
                  "lxqt"
                  "lxqt-config"
                ]
                [
                  "lxqt"
                  "lxqt-qtplugin"
                ]
                [
                  "qt6Packages"
                  "qt6ct"
                ]
                [
                  "qt6Packages"
                  "qt6gtk2"
                ]
              ];
              description = ''
                Platform theme to use for Qt applications.

                Some examples are

                `gtk`
                : Use GTK theme with
                  [`qtstyleplugins`](https://github.com/qt/qtstyleplugins)

                `gtk3`
                : Use [GTK3 integration](https://github.com/qt/qtbase/tree/dev/src/plugins/platformthemes/gtk3)
                  for file picker dialogs, font and theme configuration

                `adwaita`
                : Use Adwaita theme with
                  [`qadwaitadecorations`](https://github.com/FedoraQt/QAdwaitaDecorations)

                `gnome` (deprecated)
                : Use GNOME theme with
                  [`qgnomeplatform`](https://github.com/FedoraQt/QGnomePlatform).
                  Is no longer maintained so prefer `adwaita`.

                `lxqt`
                : Use LXQt theme style set using the
                  [`lxqt-config-appearance`](https://github.com/lxqt/lxqt-config)
                  application

                `qtct`
                : Use Qt style set using
                  [`qt5ct`](https://github.com/desktop-app/qt5ct)
                  and [`qt6ct`](https://github.com/trialuser02/qt6ct)
                  applications

                `kde`
                : Use Qt settings from Plasma
              '';
            };
            package = lib.mkOption {
              type = with lib.types; nullOr (either package (listOf package));
              default = null;
              example = lib.literalExpression "[pkgs.adwaita-qt pkgs.adwaita-qt6]";
              description = ''
                Theme package to be used in Qt5/Qt6 applications.
                Auto-detected from {option}`qt.platformTheme.name` if possible.
                See its documentation for available options.
              '';
            };
          };
        in
        lib.mkOption {
          type =
            with lib.types;
            nullOr (
              either (enum [
                "gtk"
                "gtk3"
                "gnome"
                "adwaita"
                "lxqt"
                "qtct"
                "kde"
                "kde6"
              ]) (lib.types.submodule { options = newOption; })
            );
          default = null;
          description = ''
            Deprecated. Use {option}`qt.platformTheme.name` instead.
          '';
        };
      style = {
        name = lib.mkOption {
          type = with lib.types; nullOr str;
          default = null;
          example = "adwaita-dark";
          relatedPackages = [
            "adwaita-qt"
            "adwaita-qt6"
            [
              "libsForQt5"
              "breeze-qt5"
            ]
            [
              "libsForQt5"
              "qtstyleplugin-kvantum"
            ]
            [
              "libsForQt5"
              "qtstyleplugins"
            ]
            [
              "qt6Packages"
              "qt6gtk2"
            ]
            [
              "qt6Packages"
              "qtstyleplugin-kvantum"
            ]
          ];
          description = ''
            Style to use for Qt5/Qt6 applications. Case-insensitive.

            Some examples are

            `adwaita`, `adwaita-dark`, `adwaita-highcontrast`, `adwaita-highcontrastinverse`
            : Use the Adwaita style from
              [`adwaita-qt`](https://github.com/FedoraQt/adwaita-qt)

            `breeze`
            : Use the Breeze style from
              [`breeze`](https://github.com/KDE/breeze)

            `bb10bright`, `bb10dark`, `cde`, `cleanlooks`, `gtk2`, `motif`, `plastique`
            : Use styles from
              [`qtstyleplugins`](https://github.com/qt/qtstyleplugins)

            `kvantum`
            : Use styles from
              [`kvantum`](https://github.com/tsujan/Kvantum)
          '';
        };

        package = lib.mkOption {
          type = with lib.types; nullOr (either package (listOf package));
          default = null;
          example = lib.literalExpression "pkgs.adwaita-qt";
          description = ''
            Theme package to be used in Qt5/Qt6 applications.
            Auto-detected from {option}`qt.style.name` if possible.
            See its documentation for available options.
          '';
        };
      };
    }
    // (lib.genAttrs' [ "qt5ct" "qt6ct" ] (
      name:
      lib.nameValuePair "${name}Settings" (
        lib.mkOption {
          type = lib.types.nullOr qtctFormat.type;
          default = null;
          example = lib.literalExpression ''
            {
              Appearance = {
                style = "kvantum";
                icon_theme = "Papirus-Dark";
                standar_dialogs = "xdgdesktopportal";
              };
              Fonts = {
                fixed = "\"DejaVuSansM Nerd Font Mono,12\"";
                general = "\"DejaVu Sans,12\"";
              };
            }
          '';
          description = ''
            Qtct configuration. Writes settings to `${name}/${name}.conf`
            file. Lists will be translated to comma-separated strings.
            Fonts must be quoted (see example).
          '';
        }
      )
    ));
  };

  config =
    let
      deprecateKde6 =
        name: optionPath:
        if name == "kde6" then
          lib.warn ''
            The ${optionPath} value "kde6" has been deprecated and renamed to "kde".
            Please update your configuration:
              ${optionPath} = "kde";
          '' "kde"
        else
          name;

      platformTheme =
        if (builtins.isString cfg.platformTheme) then
          {
            option = "qt.platformTheme";
            name = deprecateKde6 cfg.platformTheme "qt.platformTheme";
            package = null;
          }
        else if cfg.platformTheme == null then
          {
            option = null;
            name = null;
            package = null;
          }
        else
          {
            option = "qt.platformTheme.name";
            name = deprecateKde6 cfg.platformTheme.name "qt.platformTheme.name";
            package = cfg.platformTheme.package;
          };

      # Necessary because home.sessionVariables doesn't support mkIf
      envVars = lib.filterAttrs (n: v: v != null) {
        QT_QPA_PLATFORMTHEME =
          if (platformTheme.name != null) then
            styleNames.${platformTheme.name} or platformTheme.name
          else
            null;
        QT_STYLE_OVERRIDE = cfg.style.name;
      };

      envVarsExtra =
        let
          inherit (config.home) profileDirectory;
          qtVersions = with pkgs; [
            qt5
            qt6
          ];
          makeQtPath = prefix: (map (qt: "${profileDirectory}/${qt.qtbase.${prefix}}") qtVersions);
        in
        {
          QT_PLUGIN_PATH = makeQtPath "qtPluginPrefix";
          QML2_IMPORT_PATH = makeQtPath "qtQmlPrefix";
        };

    in
    lib.mkIf cfg.enable {
      assertions = [
        {
          assertion = platformTheme.name == "gnome" -> cfg.style.name != null && cfg.style.package != null;
          message = ''
            `qt.platformTheme.name` "gnome" must have `qt.style` set to a theme that
            supports both Qt and Gtk, for example "adwaita", "adwaita-dark", or "breeze".
          '';
        }
      ];

      warnings =
        (lib.lists.optional (
          platformTheme.option == "qt.platformTheme"
        ) "The option `qt.platformTheme` has been renamed to `qt.platformTheme.name`.")
        ++ (lib.lists.optional (
          platformTheme.name == "gnome" && platformTheme.package == null
        ) "The value `gnome` for option `${platformTheme.option}` is deprecated. Use `adwaita` instead.");

      qt.style.package = lib.mkIf (cfg.style.name != null) (
        lib.mkDefault (stylePackages.${lib.toLower cfg.style.name} or null)
      );

      home = {
        sessionVariables = envVars;
        sessionSearchVariables = envVarsExtra;
      };

      # Apply theming also to apps started by systemd.
      systemd.user.sessionVariables = envVars // {
        QT_PLUGIN_PATH = lib.concatStringsSep ":" envVarsExtra.QT_PLUGIN_PATH;
        QML2_IMPORT_PATH = lib.concatStringsSep ":" envVarsExtra.QML2_IMPORT_PATH;
      };

      home.packages =
        (lib.findFirst (x: x != [ ])
          [ ]
          [
            (lib.optionals (platformTheme.package != null) (lib.toList platformTheme.package))
            (lib.optionals (platformTheme.name != null) platformPackages.${platformTheme.name} or [ ])
          ]
        )
        ++ (lib.optionals (cfg.style.package != null) (lib.toList cfg.style.package));

      xsession.importedVariables = [
        "QT_PLUGIN_PATH"
        "QML2_IMPORT_PATH"
      ]
      ++ lib.optionals (platformTheme.name != null) [ "QT_QPA_PLATFORMTHEME" ]
      ++ lib.optionals (cfg.style.name != null) [ "QT_STYLE_OVERRIDE" ];

      xdg.configFile =
        lib.pipe
          [ "qt5ct" "qt6ct" ]
          [
            (lib.filter (qtct: cfg."${qtct}Settings" != null))
            (lib.flip lib.genAttrs' (
              qtct:
              lib.nameValuePair "${qtct}/${qtct}.conf" {
                source = qtctFormat.generate "${qtct}-config" cfg."${qtct}Settings";
              }
            ))
          ];
    };
}
