{
  config,
  lib,
  specialArgs,
  ...
}:

let
  inherit (lib)
    mkOption
    concatLists
    concatStringsSep
    mkEnableOption
    ;
  inherit (lib.types)
    listOf
    nonEmptyStr
    enum
    str
    bool
    ;
  inherit (lib.lists) optionals;
  inherit (specialArgs) rootCfg;
in
{
  options = {
    exclude = mkOption {
      type = listOf nonEmptyStr;
      default = rootCfg.exclude;
    };

    followGitignore = mkOption {
      type = bool;
      default = rootCfg.followGitignore;
    };

    sources = mkOption {
      type = listOf nonEmptyStr;
      default = [ ]; # TODO: validate if at least 1 element
      description = ''
        A list of absolute paths to the files or folders that should get synchronized.
        It isn't necessary to wrap them in escaped double quotation marks.
      '';
    };

    target = {
      location = mkOption {
        type = nonEmptyStr;
        description = ''
          The target to where the data should be backed up.
          If it is a directory, it will be created automatically by rsync.
        '';
      };

      finalLocation = mkOption {
        type = nonEmptyStr;
        default =
          if config.target.type == "local" then
            config.target.location
          else if config.target.type == "rsyncd" then
            "rsync://${config.target.host}/${config.target.location}"
          else
            throw "Unable to create location.";
      };

      type = mkOption {
        type = enum [
          "local"
          "rsyncd"
        ];
        default = "local";
        description = ''
          Guesses if the given target is local or ssh, very rudimentary therefore it might be necessary to specify manually (or rewrite the logic).
        '';
      };

      host = mkOption {
        type = str;
        default = "";
      };
    };

    interval = mkOption {
      type = nonEmptyStr;
      default = "daily";
    };

    flags = mkOption {
      type = listOf str;
      default = [
        "--archive"
        "--verbose"
        "--mkpath" # create destination's missing path components
        "--safe-links" # ignore symlinks that point outside the tree
        "--itemize-changes" # output a change-summary for all updates
      ];
    };

    flagsFinal = mkOption {
      type = listOf str;
      default = concatLists [
        config.flags
        (optionals config.incremental.enable [
          "--delete"
          "--backup-dir=\"${config.incremental.finalIncrementPath}\""
        ])
        (optionals config.followGitignore [ "--filter=':- .gitignore'" ])
        [ "--port=${toString rootCfg.port}" ]
        (map (ex: "--exclude='${ex}'") config.exclude)
      ];
      apply = concatStringsSep " ";
    };

    incremental = {
      enable = mkEnableOption "incremental backups";
      format = mkOption {
        type = nonEmptyStr;
        default = "%Y-%m-%d-%-H-%M-%S-$RANDOM";
        description = ''
          The increment folder name. Refer to `man date` for specifics about the format.
          Omit the leading +.
        '';
      };
      finalIncrementPath = mkOption {
        type = nonEmptyStr;
        default = "../increment/$(date +${config.incremental.format})/";
        description = ''
          The directory in which the changes will be stored, the .. at the start is necessary because it is relative to
          the target and we append `backup` to the target.

          -- cfg.location
              | - backup/
              | - increment/<increment format>
        '';
      };
    };
  };
}
