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

using GLib;

public class ResticPlugin : DejaDup.ToolPlugin
{
  bool has_been_setup = false;
  string version = null;

  construct
  {
    name = "restic";
  }

  public override bool needs_dependencies() {
    return Environment.find_program_in_path(restic_command()) == null;
  }

  public override string[] get_dependencies()
  {
    return Config.RESTIC_PACKAGES.split(",");
  }

  const int REQUIRED_MAJOR = 0;
  const int REQUIRED_MINOR = 17;
  const int REQUIRED_MICRO = 1;
  void do_initial_setup () throws Error
  {
    if (has_been_setup)
      return;

    this.version = discover_version();

    int major, minor, micro;
    if (!DejaDup.parse_version(version, out major, out minor, out micro))
      throw new SpawnError.FAILED(_("Could not understand restic version ‘%s’.").printf(version));

    if (!DejaDup.meets_version(major, minor, micro, REQUIRED_MAJOR, REQUIRED_MINOR, REQUIRED_MICRO)) {
      var msg = _("Backups requires at least version %d.%d.%d of restic, " +
                  "but only found version %d.%d.%d");
      throw new SpawnError.FAILED(msg.printf(REQUIRED_MAJOR, REQUIRED_MINOR, REQUIRED_MICRO, major, minor, micro));
    }

    has_been_setup = true;
  }

  string? parse_json_version(string line)
  {
    if (!line.has_prefix("{"))
      return null;
    try {
      var parser = new Json.Parser.immutable_new();
      parser.load_from_data(line);
      var root = parser.get_root();
      var object = root.get_object();
      return object.get_string_member("version");
    }
    catch (Error err) {
      return null;
    }
  }

  string discover_version() throws SpawnError
  {
    string output;
    Process.spawn_sync(null, {restic_command(), "version", "--json"}, null,
                       SpawnFlags.SEARCH_PATH, null, out output);

    // Output looks like:
    // {"version":"0.17.0","go_version":"go1.22.2","go_os":"linux","go_arch":"amd64"}

    // Let's be defensive about the future and scan for multiple lines.
    // (Maybe some random non-JSON output happens, or an exit error blob.)
    var lines = output.split("\n");
    foreach (var line in lines) {
      var version = parse_json_version(line);
      if (version != null)
        return version;
    }

    // We didn't find a JSON version.
    // Let's assume it's an old release (pre 0.17) without a JSON version.
    // Sample output: "restic 0.12.0 compiled with go1.22.2 on linux/amd64"
    var tokens = output.split(" ");
    if (tokens == null || tokens.length < 2)
      throw new SpawnError.FAILED(_("Could not understand restic version."));
    return tokens[1].strip();
  }

  public override string get_version() throws Error
  {
    do_initial_setup();
    return this.version;
  }

  public override DejaDup.ToolJob create_job() throws Error
  {
    do_initial_setup();
    return new ResticJob();
  }

  public override bool supports_backend(DejaDup.Backend.Kind kind, out string explanation)
  {
    explanation = null;

    switch(kind) {
      case DejaDup.Backend.Kind.LOCAL:
      case DejaDup.Backend.Kind.GVFS: // via fuse
      case DejaDup.Backend.Kind.GOOGLE: // via rclone
      case DejaDup.Backend.Kind.MICROSOFT: // via rclone
      case DejaDup.Backend.Kind.RCLONE:
        return true;

      default:
        explanation = _("This storage location is not yet supported.");
        return false;
    }
  }

  public static string restic_command()
  {
    var testing_str = Environment.get_variable("DEJA_DUP_TESTING");
    if (testing_str != null && int.parse(testing_str) > 0)
      return "restic";
    else
      return Config.RESTIC_COMMAND;
  }

  public override bool supports_mount() {return true;}
}
