/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 2 -*-
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 * SPDX-FileCopyrightText: Michael Terry
 */

// This file holds some abstractions around loading which ToolPlugin to use.
// (i.e. whether to use Restic or Duplicity)

using GLib;

namespace DejaDup {

string unsupported_msg(string tool_name)
{
  // Translators: '%s' is a backup tool name like Restic or Borg
  return _("%s backups are not supported in this release. Try using another storage location.").printf(tool_name);
}

#if ENABLE_BORG
ToolPlugin borg_tool = null;
#endif
ToolPlugin make_borg_tool() throws OptionError
{
#if ENABLE_BORG
  if (borg_tool == null)
    borg_tool = new BorgPlugin();
  return borg_tool;
#else
  throw new OptionError.BAD_VALUE(unsupported_msg("Borg"));
#endif
}

ToolPlugin restic_tool = null;
internal ToolPlugin make_restic_tool()
{
  if (restic_tool == null)
    restic_tool = new ResticPlugin();
  return restic_tool;
}

ToolPlugin duplicity_tool = null;
ToolPlugin make_duplicity_tool()
{
  if (duplicity_tool == null)
    duplicity_tool = new DuplicityPlugin();
  return duplicity_tool;
}

ToolPlugin make_tool(string tool_name) throws OptionError
{
  switch(tool_name)
  {
    case "borg":
      return make_borg_tool();
    case "duplicity":
      return make_duplicity_tool();
    case "auto":
      return make_restic_tool();
    default:
      return make_restic_tool();
  }
}

public ToolPlugin get_default_tool()
{
  var settings = get_settings();
  var tool_name = settings.get_string(TOOL_WHEN_NEW_KEY);
  try {
    return make_tool(tool_name);
  } catch (OptionError err) {
    return make_duplicity_tool();
  }
}

// Backend must already have prepare() called. This will inspect the files
// in the backend location to see which tool is being used there.
public async ToolPlugin? get_tool_for_backend(Backend backend) throws OptionError
{
  var filenames = yield backend.peek_at_files();
  filenames.sort(strcmp);

  // If the folder is empty, no tool present
  if (filenames == null)
    return null;

  // The basic approach is: look for files we recognize, and try to ignore
  // some files we don't notice. (Hidden files are already ignored.)
  // This covers some edge cases of random files being thrown in there, but
  // will still complain about widespread dual-uses of the folder, which we
  // largely want to allow to be completely owned by the underlying tool.
  // Note that the peek_at_files() call above only guarantees that we'll see 20
  // of the files, so if there *is* widespread dual-use of the folder, we may
  // only see a partial view.

  // Restic repos have these entries:
  //   config data/ index/ keys/ snapshots/ (and sometimes locks/)
  // Let's check for those, but allow other files, to try to be a little
  // future proof in case Restic expands its file format on us.
  string[] expected = {"config", "data", "index", "keys", "snapshots"};
  int restic_found = 0;
  unowned List<string> iter = filenames;
  while (iter != null) {
    unowned List<string> next = iter.next;
    for (int i = 0; i < expected.length; i++) {
      if (iter.data == expected[i]) {
        filenames.delete_link(iter); // avoid complaining later about this file
        restic_found++;
        break;
      }
    }
    iter = next;
  }
  if (restic_found == expected.length)
    return make_tool("restic");

  // All duplicity files have the same basic prefix. (We also must allow for a
  // legacy "restic" folder. While we're at it, allow other random files too.)
  foreach (var filename in filenames) {
    if (filename.has_prefix("duplicity-"))
      return make_tool("duplicity");
  }

  // Error case (unrecognized files)
  var msg = _("Unrecognized file in storage location: '%s'. Choose an empty folder instead.").printf(filenames.data);
  throw new OptionError.FAILED(msg);
}

}
