{
  lib,
  config,
  nixosConfig,
  sysLib,
  pkgs,
  ...
}: let
  unisonPath = "${config.xdg.dataHome}/unison";

  # These are only used for the script
  unisonOptions = {
    sshcmd = "ssh";
    ui = "text";
    auto = "true";
    # This is useless, with hm links
    links = "false";
  };

  paths_to_keep = [
    "~/.local/state/mpv"
    "~/.local/state/nvim"
    "~/.local/share"
    "~/.local/.Trash-1000"

    "~/.mozilla/.Trash-1000"
    "~/.mozilla/firefox"

    "~/media"
    "~/school"
    "~/repos"
  ];
  paths_to_ignore = [
    # already synchronized by the taskserver
    "~/.local/share/task"

    # Should not be synchronized
    "~/.local/share/unison"
  ];

  hostName = let
    hn = nixosConfig.networking.hostName;
  in
    if hn == "tiamat"
    then "apzu"
    else if hn == "apzu"
    then "tiamat"
    else builtins.throw "Host (${hn}) not yet covered in the unison host mapping.";

  unitName = name: builtins.replaceStrings ["/"] ["-"] name;

  mkPath = path:
    if lib.strings.hasPrefix "~" path
    then "${builtins.elemAt (builtins.attrNames config.home.persistence)
      0}${lib.strings.removePrefix "~" path}"
    else
      builtins.throw
      "Every pathname needs to start with a '~'";

  mkPair = pathname: let
    path = mkPath pathname;
  in {
    name = unitName "${pathname}";
    value = {
      stateDirectory = unisonPath;
      roots = [
        "${path}"
        "ssh://${config.home.username}@${hostName}.fritz.box/${path}"
      ];
    };
  };

  getIgnoredSingle = path: path_to_ignore: let
    clean_path_to_ignore = mkPath path_to_ignore;
    commonPath = builtins.substring 0 (builtins.stringLength path) clean_path_to_ignore;
  in
    if commonPath == path
    then let
      preFinalPath =
        builtins.substring (builtins.stringLength commonPath)
        (builtins.stringLength clean_path_to_ignore)
        clean_path_to_ignore;
      finalPath =
        if lib.strings.hasPrefix "/" preFinalPath
        then lib.strings.removePrefix "/" preFinalPath
        else preFinalPath;
    in "BelowPath ${finalPath}"
    else null;

  getIgnored = paths_to_ignore: path:
    serialiseArgs {
      ignore =
        builtins.filter (x: x != null) (builtins.map (getIgnoredSingle path) paths_to_ignore);
    };

  serialiseArg = key: val:
    if builtins.typeOf val == "string"
    then lib.strings.escapeShellArg "-${key}=${lib.strings.escape ["="] val}"
    else if builtins.typeOf val == "list"
    then lib.strings.concatStringsSep " " (builtins.map (serialiseArg key) val)
    else builtins.throw "Unsupported type: ${builtins.typeOf val}";

  serialiseArgs = args:
    lib.strings.concatStringsSep " " (
      lib.attrsets.mapAttrsToList
      serialiseArg
      args
    );

  esa = a: lib.strings.escapeShellArg a;

  mkScriptLine = pathname: let
    path =
      mkPath pathname;
  in
    lib.strings.concatStringsSep " " [
      "unison"
      "${serialiseArgs unisonOptions}"
      "$EXTRA_OPTIONS"
      "${getIgnored paths_to_ignore path}"
      "${esa path}"
      (esa "ssh://${config.home.username}@${hostName}.fritz.box/${path}")
    ];

  script = lib.strings.concatStringsSep "\n" (builtins.map mkScriptLine paths_to_keep);

  pairs = builtins.listToAttrs (builtins.map mkPair paths_to_keep);
in {
  home.sessionVariables = {
    UNISON = unisonPath;
  };
  home.packages = [
    pkgs.unison
    (sysLib.writeShellScript {
      name = "unison-sync";
      src = builtins.toFile "unison-backup" (''
          #!/usr/bin/env dash

          # shellcheck source=/dev/null
          SHELL_LIBRARY_VERSION="2.1.1" . %SHELL_LIBRARY_PATH

          export UNISON=${esa unisonPath};

          if [ "$1" = "links" ]; then
            EXTRA_OPTIONS="-links=true";
          fi
          EXTRA_OPTIONS="$EXTRA_OPTIONS $*"
        ''
        + script);
      dependencies = with pkgs; [unison openssh];
    })
  ];
  services.unison = {
    enable = false;
    inherit pairs;
  };
}