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

# todo : this needs to be cleaned up

with lib;

let

  cfg = config.services.browser;

  chromiumBin = "${pkgs.chromium}/bin/chromium";
  chromeBin = "${pkgs.google-chrome}/bin/google-chrome-stable";
  firefoxBin = "${pkgs.firefox}/bin/firefox";
  tarBin = "${pkgs.gnutar}/bin/tar";

  # desktop file
  # ------------
  # makes it possible to be used by other programs
  desktopFile =
    bin:
    let
      browserName = bin.name;
    in
    pkgs.writeTextFile {
      name = "${browserName}.desktop";
      destination = "/share/applications/${browserName}.desktop";
      text = ''
        [Desktop Entry]
        Type=Application
        Exec=${bin}/bin/${browserName} %U
        Icon=chromium
        Comment=An open source web browser from Google
        Terminal=false
        Name=${browserName}
        GenericName=Web browser
        MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;x-scheme-handler/webcal;x-scheme-handler/about
        Categories=Network;WebBrowser
        StartupWMClass=${browserName}
      '';
    };

  killBrowser =
    name: pkgs.writeShellScriptBin "${name}-kill" "sudo ${pkgs.killall}/bin/killall -9 -u ${name}";

  cleanBrowser =
    name: browser: home: homeBackup:
    let
      backupFile = "${homeBackup}.tar.lzma";
      rolloutFile = "${home}.tar.lzma";
      lockFile = "${home}-lock";
    in
    pkgs.writeShellScriptBin "${name}-clean" # sh
      ''
        sudo ${pkgs.killall}/bin/killall -9 -u ${name}
        sudo rm -f ${lockFile}
        sudo rm -rf ${home}
      '';

  createBrowser =
    name: user: browser: home: homeBackup:
    let
      backupFile = "${homeBackup}.tar.lzma";
      rolloutFile = "${home}.tar.lzma";
      lockFile = "${home}-lock";
    in
    pkgs.writeShellScriptBin "${name}" # sh
      ''
        # set -x
        if [[ ! -e ${lockFile} ]]
        then
            # rollout backup
            if [[ -e ${backupFile} ]]
            then
                if [[ ! -d ${home} ]]
                then
                    # todo : use make user
                    sudo mkdir -p ${home}
                    sudo chown -R ${user}:users ${home}
                fi
                cp ${backupFile} ${rolloutFile}
                sudo -u ${user} ${tarBin} xf ${rolloutFile} --directory ${home}
                rm ${rolloutFile}
                touch ${lockFile}
            fi
        fi

        sudo -u ${user} ${browser}
      '';

  browserExecutableList =
    let
      allBrowser = flip mapAttrsToList cfg.configList (
        name: config:
        let
          browser =
            if config.browserType == "chrome" then
              ''${chromiumBin} "$@"''
            else if config.browserType == "google" then
              ''${chromeBin} "$@"''
            else
              ''${firefoxBin} "$@"'';
        in
        createBrowser name config.user browser config.home config.homeBackup
      );
      xclipBrowser = [
        (pkgs.writeShellScriptBin "copy-to-xclip" # sh
          ''
            echo "$*" | ${pkgs.xclip}/bin/xclip
          ''
        )
      ];
    in
    allBrowser ++ xclipBrowser;

  createBackupScript =
    name: home: backupHome:
    createBackupScriptTemplate {
      sudo = name;
      name = name;
      source = home;
      target = backupHome;
      excludes = [
        "Downloads"
        ".cache"
        ".config/*chrom*/*cache"
        ".config/*chrom*/Crash Reports"
        ".config/*chrom*/Default/DawnCache"
        ".config/*chrom*/Default/IndexedDB"
        ".config/*chrom*/Default/Local Storage"
        ".config/*chrom*/Default/Service Worker/CacheStorage"
        ".config/*chrom*/Default/Storage"
        ".config/*chrom*/Default/optimization_guide_prediction_model_downloads"
        ".mozilla/firefox/*.default/*cache*"
        ".mozilla/firefox/*.default/crashes"
        ".mozilla/firefox/*.default/datareporting"
        ".mozilla/firefox/*.default/sessionstore-backups"
        ".mozilla/firefox/*.default/storage-sync-v2.sqlite"
        ".mozilla/firefox/*.default/storage/temporary"
        ".mozilla/firefox/*.default/storage/to-be-removed"
        ".mozilla/firefox/Crash Reports"
      ];
    };

  createBackupScriptTemplate =
    {
      sudo,
      name,
      source,
      target,
      excludes ? [ ],
    }:
    pkgs.writers.writeBashBin "${name}-backup" ''
      sudo -u ${sudo} \
      ${tarBin} \
      ${concatStringsSep " " (map (pattern: "--exclude=\"${pattern}\"") excludes)} \
      --create \
      --verbos \
      --lzma \
      --file ${source}.tar.lzma \
      --directory ${source} \
      .

      cp ${source}.tar.lzma ${target}.tar.lzma
    '';

  allBackupScripts =
    let
      filteredConfigs = filterAttrs (
        name: browserConfig: browserConfig.homeBackup != null
      ) cfg.configList;
    in
    mapAttrsToList (
      name: browserConfig: createBackupScript name browserConfig.home browserConfig.homeBackup
    ) filteredConfigs;

  allCleanScripts =
    let
      filteredConfigs = filterAttrs (
        name: browserConfig: browserConfig.homeBackup != null
      ) cfg.configList;
    in
    mapAttrsToList (
      name: browserConfig: cleanBrowser name name browserConfig.home browserConfig.homeBackup
    ) filteredConfigs;

  allKillScripts = mapAttrsToList (name: _: killBrowser name) cfg.configList;

  # browser chooser
  # ---------------
  browserSelect = pkgs.writeScriptBin "browser-select" ''
    # select a browser using dmenu
    # ----------------------------
    BROWSER=$( echo -e "${lib.concatMapStringsSep "\\n" (bin: bin.name) browserExecutableList}" \
      | ${pkgs.rofi}/bin/rofi -dmenu )

    # start selected browser
    # ----------------------
    case $BROWSER in
    ${lib.concatStringsSep "\n" (
      flip map browserExecutableList (bin: "${bin.name}) export BIN=${bin}/bin/${bin.name} ;;")
    )}
    *) exit 0 ;;
    esac
    $BIN "$@"
  '';

in
{

  options.services.browser = {
    enable = mkEnableOption "enable browsers";
    # todo : it's not a list
    configList = mkOption {
      default = { };
      type =
        with types;
        attrsOf (
          submodule (
            { name, ... }:
            {
              options = {
                browserType = mkOption {
                  type =
                    with types;
                    enum [
                      "firefox"
                      "chrome"
                      "google"
                    ];
                  default = "chrome";
                  description = ''
                    the type of browser which is simulated
                  '';
                };
                home = mkOption {
                  type = with types; str;
                  description = ''
                    Home of the browser.
                  '';
                };
                gpu = mkOption {
                  type = with types; bool;
                  default = true;
                  description = ''
                    add browser user to video group so give browser rights to use gpu.
                    see : chrome://gpu/
                  '';
                };
                user = mkOption {
                  default = name;
                  type = with types; str;
                  description = ''
                    user to run the browser as
                  '';
                };
                sudoUsers = mkOption {
                  type = with types; listOf str;
                  description = ''
                    user allowed to run sudo without password to start the browser
                  '';
                };
                homeBackup = mkOption {
                  type = with types; nullOr str;
                  default = null;
                  example = "~/.my-browser-backup";
                  description = ''
                    backup of the home, which gets rolled out if the
                    home does not exists. usefull for homes in tmpfs.
                    dont use file endings!
                  '';
                };
              };
            }
          )
        );
    };
  };

  config = mkIf cfg.enable {

    # add sudo rights
    security.sudo.extraConfig =
      let
        extraRules = flip mapAttrsToList cfg.configList (
          name: values:
          concatStringsSep "" (
            map (sudoUser: ''
              # sudo configuration to control browser
              ${sudoUser} ALL=(${values.user}) NOPASSWD: ALL
              ${sudoUser} ALL=(root) NOPASSWD: /run/current-system/sw/bin/mkdir -p ${values.home}
              ${sudoUser} ALL=(root) NOPASSWD: /run/current-system/sw/bin/chown -R ${values.user}\:users ${values.home}
              ${sudoUser} ALL=(root) NOPASSWD: ${pkgs.killall}/bin/killall -9 -u ${name}
              ${sudoUser} ALL=(root) NOPASSWD: /run/current-system/sw/bin/rm -rf ${values.home}
              ${sudoUser} ALL=(root) NOPASSWD: /run/current-system/sw/bin/rm -f ${values.home}-lock
            '') values.sudoUsers
          )
        );
      in
      lib.concatStringsSep "\n" extraRules;

    # create users
    users.users =
      flip mapAttrs cfg.configList (
        name: config: {
          home = config.home;
          createHome = false;
          initialPassword = "${name} -browser";
          shell = pkgs.bashInteractive;
          isNormalUser = false;
          isSystemUser = true;
          group = "users";
          # enable video usage
          extraGroups = [
            "audio"
            "pipewire"
          ] ++ (if config.gpu then [ "video" ] else [ ]);
        }
      )
      // {
        # add groups to mainUser
        mainUser.extraGroups = builtins.attrNames cfg.configList;
      };

    environment.systemPackages =
      [
        browserSelect
        (desktopFile browserSelect)
      ]
      ++ browserExecutableList
      ++ (map (bin: desktopFile bin) browserExecutableList)
      ++ allBackupScripts
      ++ allCleanScripts
      ++ allKillScripts;

  };
}