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

/**
 * This backend is for the consumer-focused storage offering by Microsoft.
 * At the time of this writing, it is called Microsoft OneDrive.
 *
 * https://docs.microsoft.com/en-us/onedrive/developer/rest-api
 */

using GLib;

namespace DejaDup {

public const string MICROSOFT_ROOT = "Microsoft";
public const string MICROSOFT_FOLDER_KEY = "folder";

public const string MICROSOFT_SERVER = "microsoft.com";

public class BackendMicrosoft : BackendOAuth
{
  public string drive_id {get; private set;}

  public BackendMicrosoft(Settings? settings) {
    Object(kind: Kind.MICROSOFT,
           settings: (settings != null ? settings : get_settings(MICROSOFT_ROOT)));
  }

  Rclone rclone;
  construct {
    // OAuth class properties
    brand_name = "Microsoft";
    client_id = Config.MICROSOFT_CLIENT_ID;
    auth_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
    token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
    scope = "offline_access Files.ReadWrite";
  }

  public override string get_redirect_uri()
  {
    // This 'auth' path is required
    return "msal%s://auth".printf(client_id);
  }

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

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

  public override Icon? get_icon() {
    return new ThemedIcon("deja-dup-microsoft-onedrive");
  }

  public override async bool is_ready(out string reason, out string message) {
    reason = "microsoft-reachable";
    message = _("Backup will begin when a network connection becomes available.");
    return yield Network.get().can_reach("https://%s/".printf(MICROSOFT_SERVER));
  }

  internal string get_folder() {
    return get_folder_key(settings, MICROSOFT_FOLDER_KEY);
  }

  public override string get_location_pretty()
  {
    var folder = get_folder();
    if (folder == "")
      return _("Microsoft OneDrive");
    else
      // Translators: %s is a folder.
      return _("%s on Microsoft OneDrive").printf(folder);
  }

  protected override string get_unique_location()
  {
    return "microsoft:" + get_folder();
  }

  public override async void cleanup()
  {
    rclone = null;
  }

  public override async void get_space(out uint64 free, out uint64 total)
  {
    yield Rclone.get_space(this, out free, out total);
  }

  public override async List<string> peek_at_files()
  {
    return yield Rclone.list_files(this, 20);
  }

  protected override async void got_credentials() throws Error
  {
    if (get_folder() == "") {
      // Duplicity requires a folder, and this is a reasonable restriction.
      throw new IOError.FAILED("%s", _("You must provide a Microsoft OneDrive folder."));
    }

    // Grab the drive ID in case a tool needs it
    var message = new Soup.Message(
      "GET", "https://graph.microsoft.com/v1.0/me/drive?select=id"
    );
    var reader = yield send_message(message);
    reader.read_member("id");
    drive_id = reader.get_string_value();
    reader.end_member();
  }

  public string fill_envp(ref List<string> envp)
  {
    // Setting the config path to empty prevents Rclone from trying to read
    // from its default config file (which we don't need, but might be
    // encrypted and thus cause an error).
    envp.append("RCLONE_CONFIG=");

    envp.append("RCLONE_ONEDRIVE_CLIENT_ID=" + client_id);
    envp.append("RCLONE_ONEDRIVE_TOKEN=" + full_token);
    envp.append("RCLONE_ONEDRIVE_DRIVE_ID=" + drive_id);
    envp.append("RCLONE_ONEDRIVE_DRIVE_TYPE=personal");
    return ":onedrive:" + get_folder();
  }
}

} // end namespace

