{
  config,
  pkgs,
  lib,
  ...
}: let
  inherit
    (lib)
    mkOption
    mkForce
    getExe
    getExe'
    listToAttrs
    flatten
    mapAttrsToList
    mapAttrs
    mapAttrs'
    nameValuePair
    toLower
    replaceStrings
    concatMapStringsSep
    ;
  partOf = cfg: "${replaceStrings [" "] ["-"] (toLower cfg.name)}.target";
  # make a firefox webapp + hidden .desktop entry for the client app
  make-firefox = cfg:
    mapAttrs' (
      name: cfg:
        nameValuePair "${name}-client"
        {
          inherit (cfg) name;
          url = "http://127.0.0.1:${builtins.toString cfg.port}";
          extraSettings = config.programs.firefox.profiles.default.settings;
          hidden = true;
        }
    )
    cfg;
  # make a systemd service for running the frontend
  make-systemd-service = cfg:
    mapAttrs' (
      name: cfg:
        if
          (cfg.service
            != null)
        then
          nameValuePair "${cfg.name}-frontend" {
            Unit = {
              Description = "${cfg.name} Frontend";
              WantedBy = mkForce [];
            };

            Service = cfg.service;
          }
        else nameValuePair "" {}
    )
    cfg;
  # modify systemd units to be PartOf this target
  modify-systemd-services = cfg:
    listToAttrs (flatten (mapAttrsToList (
        name: cfg: (map (
            req: {
              name = "${req}";
              value = {
                Unit = {
                  PartOf = partOf cfg;
                };
              };
            }
          )
          cfg.requires.services)
      )
      cfg));
  modify-quadlets = cfg:
    listToAttrs (flatten (mapAttrsToList (
        name: cfg: (map (
            req: {
              name = "${req}";
              value = {
                unitConfig = {
                  PartOf = partOf cfg;
                };
              };
            }
          )
          cfg.requires.containers)
      )
      cfg));
  # make a systemd target to collate dependencies
  make-systemd-target = cfg:
    mapAttrs (
      name: cfg: {
        Unit = {
          Description = "${cfg.name} Target";
          WantedBy = mkForce [];
          Requires =
            (map (req: req + ".service") cfg.requires.services)
            ++ (map (req: "podman-" + req + ".service") cfg.requires.containers);
        };
      }
    )
    cfg;
  # make desktop shortcuts and a script which will handle starting everything
  make-xdg = cfg:
    mapAttrs (
      name: cfg: {
        inherit (cfg) name icon genericName;
        type = "Application";
        exec = "${let
          notify-send = "${getExe' pkgs.libnotify "notify-send"} -a \"${cfg.name}\"";
          systemctl = "${getExe' pkgs.systemd "systemctl"}";
          dex = "${getExe pkgs.dex}";
          podman = "${getExe pkgs.podman}";
          makeContainerCheck = container: ''[ "$(${podman} inspect -f {{.State.Health.Status}} ${container})" == "healthy" ]'';
          # makeContainerCheck = container: ''
          #   [ ${podman} inspect -f {{.State.Status}} ${container})" != "running" ]
          # '';
          containerChecks =
            if (cfg.requires.containers != [])
            then
              ''
                container_checks() {
                  if ''
              + (concatMapStringsSep " && "
                (container: makeContainerCheck container)
                cfg.requires.containers)
              + ''
                  ; then
                    return 0
                  else
                    return 1
                  fi
                }
              ''
            else ''
              container_checks() {
                return 0
              }
            '';
        in
          pkgs.writeShellScript "${name}"
          ''
            set -euo pipefail

            exit_error() {
              ${notify-send} -w "Failure" $1
              exit 1
            }

            ${containerChecks}

            ${notify-send} "Launching ${name} backend.." "Please be patient."
            ${systemctl} --user start ${name}.target || exit_error "Failed to launch!"

            checks=0
            until container_checks; do
              sleep 2
              checks=$((checks+1))
              if [ $((checks%10)) -eq 0 ]; then
                ${notify-send} "Waiting for backend."
              fi
              if [ $checks -ge 60 ]; then
                ${systemctl} --no-block --user stop ${name}.target
                exit_error "Failed to launch!"
              fi
            done

            ${notify-send} "Launching ${name}.."
            ${dex} -w ~/.nix-profile/share/applications/${name}-client.desktop

            ${notify-send} "Goodbye" "Shutting down."
            ${systemctl} --user stop ${name}.target
            exit 0
          ''}";
      }
    )
    cfg;
  cfg = config.localWebApps;
in {
  options.localWebApps = mkOption {
    default = {};
    type = with lib.types;
      attrsOf (submodule {
        options = {
          name = mkOption {
            type = str;
            description = "Display name of the webapp.";
          };

          genericName = mkOption {
            type = nullOr str;
            description = "Generic name of the webapp.";
            default = null;
          };

          icon = mkOption {
            type = nullOr (either str path);
            description = "Path to a file to use for application icon.";
            default = null;
          };

          requires = mkOption {
            type = nullOr (submodule {
              options = {
                containers = mkOption {
                  type = listOf str;
                  default = [];
                };
                services = mkOption {
                  type = listOf str;
                  default = [];
                };
              };
            });
            default = null;
            description = "Containers or services this app requires.";
          };

          service = mkOption {
            type = nullOr (submodule {
              options = {
                execStartPre = mkOption {
                  type = nullOr str;
                  default = null;
                };
                execStart = mkOption {
                  type = nullOr str;
                  default = null;
                };
                execStop = mkOption {
                  type = nullOr str;
                  default = null;
                };
              };
            });
            default = null;
            description = "Submodule containing exec[StartPre/Start/Stop] commands for any required systemd service";
          };

          port = mkOption {
            type = int;
            description = "Local port the webapp should host on.";
          };
        };
      });
  };

  config = {
    programs.firefox.webapps = make-firefox cfg;

    systemd.user.targets = make-systemd-target cfg;
    systemd.user.services = (make-systemd-service cfg) // (modify-systemd-services cfg);
    services.podman.containers = modify-quadlets cfg;

    xdg.desktopEntries = make-xdg cfg;
  };
}
