# TODO: https://github.com/lilyinstarlight/foosteros/blob/dfe1ab3eb68bfebfaa709482d52fa04ebdde81c8/config/restic.nix#L23 <- this is better
{
  config,
  pkgs,
  lib,
  ...
}:
let
  inherit (lib)
    mkEnableOption
    mkOption
    mkDefault
    mkIf
    types
    getExe
    ;
  cfg = config.custom.restic;
  mapBtrfsRoots =
    rootDir:
    let
      backupDir = lib.removeSuffix "/" "/backup${rootDir}";
      slash = if rootDir == "/" then "" else "/";
      awk = getExe pkgs.gawk;
      continueIfInExclude = ''
        exclude_subv="${toString cfg.btrfsExcludeSubvolume}"
        found=false
        for subv in $exclude_subv; do
            if [[ "$subvol" == "$subv" ]]; then
                found=true
                echo "$subvol is in exclude subvolumes, skipped"
                break
            fi
        done
        $found && continue
      '';
    in
    {
      backupPrepareCommand = ''
        echo "Creating snapshot for ${rootDir}"
        subvolumes=$(${pkgs.btrfs-progs}/bin/btrfs subvolume list -o "${rootDir}" | ${awk} '{print $NF}')
        mkdir -p "${backupDir}"
        ${pkgs.btrfs-progs}/bin/btrfs subvolume snapshot -r "${rootDir}" "${backupDir}/rootDirectory"
        for subvol in $subvolumes; do
            ${continueIfInExclude}
            [[ /"$subvol" == "${backupDir}"* ]] && continue

            snapshot_path=$(dirname "${backupDir}/$subvol")
            mkdir -p "$snapshot_path"

            echo "Creating snapshot for subvolume: $subvol at $snapshot_path"
            ${pkgs.btrfs-progs}/bin/btrfs subvolume snapshot -r "${rootDir}${slash}$subvol" "$snapshot_path"
        done
      '';

      # Note that all the manually created snapshots under backupDir will also be cleaned
      backupCleanupCommand = ''
        # Only find snapshots under backup directory
        subvolumes=$(${pkgs.btrfs-progs}/bin/btrfs subvolume list -s -o "${backupDir}" | ${awk} '{print $NF}')
        for subvol in $subvolumes; do
            echo "Removing snapshot for subvolume: $subvol"
            ${pkgs.btrfs-progs}/bin/btrfs subvolume delete "$subvol"
        done
      '';
    };

  btrfsFs = lib.attrsets.filterAttrs (
    n: v: v.fsType == "btrfs" && ((isNull cfg.btrfsRoots) || (builtins.elem n cfg.btrfsRoots))
  ) config.fileSystems;
  btrfsFsRoot = builtins.attrNames btrfsFs;
  btrfsCommands = (map mapBtrfsRoots btrfsFsRoot);
in
{
  options = {
    custom.restic = {
      enable = mkEnableOption "restic";
      paths = mkOption {
        type = types.listOf types.str;
        default = [
          "/home"
          "/var/lib"
        ];
      };
      prune = mkEnableOption "auto prune remote restic repo";
      btrfsRoots = mkOption {
        type = types.nullOr (types.listOf types.str);
        default = [ "/" ];
        description = ''
          Includeded btrfs roots. `null` means snapshot all btrfs filesystems under config.fileSystems.
        '';
      };
      btrfsExcludeSubvolume = mkOption {
        type = types.listOf types.str;
        default = [
          "nix"
          "rootfs"
          "swap"
          "var/tmp"
        ];
        example = lib.literalExpression ''
          [ "var/tmp" "srv" ]
        '';
      };
      backupPrepareCommand = mkOption {
        type = types.listOf types.str;
      };
      backupCleanupCommand = mkOption {
        type = types.listOf types.str;
      };
    };
  };
  config = mkIf cfg.enable {
    services.restic.backups.${config.networking.hostName} = {
      repositoryFile = config.sops.secrets."restic/repo_url".path;
      passwordFile = config.sops.secrets."restic/repo_password".path;
      exclude = [
        "**/.cache"
        "**/.local/share/Steam"
        "**/.local/share/flatpak"

        "**/.cargo"
        "**/.rustup"

        "**/node_modules"

        "*.pyc"
        "*.pyo"
        "**/__pycache__"
        "**/.virtualenvs"
        "**/.venv"

        # temp files / lock files
        "*.sqlite-wal"
        "*.sqlite-shm"
        "*.db-wal"
        "*.db-shm"
      ];
      timerConfig = {
        OnCalendar = "00:05";
        RandomizedDelaySec = "5h";
      };
      pruneOpts = mkIf cfg.prune [
        "--keep-daily 7"
        "--keep-weekly 5"
        "--keep-monthly 12"
        "--keep-yearly 75"
      ];
      paths = mkDefault cfg.paths;
      initialize = true;
      backupPrepareCommand = lib.strings.concatLines cfg.backupPrepareCommand;
      backupCleanupCommand = lib.strings.concatLines cfg.backupCleanupCommand;
    };
    custom.restic.backupPrepareCommand = map (x: x.backupPrepareCommand) btrfsCommands;
    custom.restic.backupCleanupCommand = map (x: x.backupCleanupCommand) btrfsCommands;
  };
}
