// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/password_manager/android/password_sync_controller_delegate_android.h"

#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/sequenced_task_runner.h"
#include "components/password_manager/core/browser/password_store/android_backend_error.h"
#include "components/password_manager/core/browser/password_sync_util.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_user_settings.h"

namespace password_manager {

namespace {

std::string BuildCredentialManagerNotificationMetricName(
    const std::string& suffix) {
  return "PasswordManager.SyncControllerDelegateNotifiesCredentialManager." +
         suffix;
}

}  // namespace

PasswordSyncControllerDelegateAndroid::PasswordSyncControllerDelegateAndroid(
    std::unique_ptr<PasswordSyncControllerDelegateBridge> bridge)
    : bridge_(std::move(bridge)) {
  DCHECK(bridge_);
  bridge_->SetConsumer(weak_ptr_factory_.GetWeakPtr());
}

PasswordSyncControllerDelegateAndroid::
    ~PasswordSyncControllerDelegateAndroid() = default;

void PasswordSyncControllerDelegateAndroid::SetSyncObserverCallbacks(
    base::RepeatingClosure on_pwd_sync_state_changed,
    base::OnceClosure on_sync_shutdown) {
  on_pwd_sync_state_changed_ = std::move(on_pwd_sync_state_changed);
  on_sync_shutdown_ = std::move(on_sync_shutdown);
}

void PasswordSyncControllerDelegateAndroid::OnSyncServiceInitialized(
    syncer::SyncService* sync_service) {
  sync_observation_.Observe(sync_service);
  UpdateCredentialManagerSyncStatus(sync_service);
}

void PasswordSyncControllerDelegateAndroid::OnStateChanged(
    syncer::SyncService* sync) {
  UpdateCredentialManagerSyncStatus(sync);
}

void PasswordSyncControllerDelegateAndroid::OnSyncShutdown(
    syncer::SyncService* sync) {
  sync_observation_.Reset();
  if (!on_sync_shutdown_) {
    return;
  }
  std::move(on_sync_shutdown_).Run();
}

void PasswordSyncControllerDelegateAndroid::OnCredentialManagerNotified() {
  base::UmaHistogramBoolean(
      BuildCredentialManagerNotificationMetricName("Success"), 1);
}

void PasswordSyncControllerDelegateAndroid::OnCredentialManagerError(
    const AndroidBackendError& error,
    int api_error_code) {
  base::UmaHistogramBoolean(
      BuildCredentialManagerNotificationMetricName("Success"), 0);
  base::UmaHistogramEnumeration(
      BuildCredentialManagerNotificationMetricName("ErrorCode"), error.type);
  if (error.type == AndroidBackendErrorType::kExternalError) {
    base::UmaHistogramSparse(
        BuildCredentialManagerNotificationMetricName("APIErrorCode"),
        api_error_code);
  }
}

void PasswordSyncControllerDelegateAndroid::UpdateCredentialManagerSyncStatus(
    syncer::SyncService* sync_service) {
  CHECK(sync_service);
  IsPwdSyncEnabled is_enabled = IsPwdSyncEnabled(
      password_manager::sync_util::HasChosenToSyncPasswords(sync_service));

  // Unfortunately, the Android password backend doesn't provide a signal of
  // when trusted vault related errors are resolved. SyncService, however, uses
  // the same infrastructure to fetch trusted vault keys on Android as the
  // Android password backend. Thus, we take the signal received from sync to
  // notify clients to re-try fetching passwords in case the trusted vault error
  // state changed (via the `on_pwd_sync_state_changed_` callback).
  HasTrustedVaultError has_trusted_vault_error = HasTrustedVaultError(
      sync_service->GetUserSettings()->IsTrustedVaultKeyRequired());

  if (credential_manager_sync_setting_.has_value() &&
      credential_manager_sync_setting_ == is_enabled) {
    // Sync setting didn't change, check for changed trusted vault error.
    if (!sync_service_has_trusted_vault_error_.has_value() ||
        sync_service_has_trusted_vault_error_ == has_trusted_vault_error) {
      // No change, nothing to notify.
      return;
    }
  }

  sync_service_has_trusted_vault_error_ = has_trusted_vault_error;

  if (on_pwd_sync_state_changed_) {
    on_pwd_sync_state_changed_.Run();
  }

  if (credential_manager_sync_setting_ != is_enabled) {
    credential_manager_sync_setting_ = is_enabled;
    if (is_enabled) {
      bridge_->NotifyCredentialManagerWhenSyncing(
          sync_service->GetAccountInfo().email);
    } else {
      bridge_->NotifyCredentialManagerWhenNotSyncing();
    }
  }
}

}  // namespace password_manager
