// Copyright 2020 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/all_passwords_bottom_sheet_controller.h"

#include <memory>
#include <vector>

#include "chrome/browser/password_manager/chrome_password_manager_client.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/chrome_password_reuse_detection_manager_client.h"
#include "chrome/browser/ui/android/passwords/all_passwords_bottom_sheet_view.h"
#include "chrome/browser/ui/android/passwords/all_passwords_bottom_sheet_view_impl.h"
#include "components/device_reauth/device_authenticator.h"
#include "components/password_manager/content/browser/content_password_manager_driver.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_store/password_form_converters.h"
#include "components/password_manager/core/browser/password_store/password_store_interface.h"
#include "content/public/browser/web_contents.h"
#include "ui/gfx/native_ui_types.h"

using autofill::mojom::FocusedFieldType;
using password_manager::PasswordManagerClient;
using safe_browsing::PasswordReuseDetectionManagerClient;

// No-op constructor for tests.
AllPasswordsBottomSheetController::AllPasswordsBottomSheetController(
    base::PassKey<class AllPasswordsBottomSheetControllerTest>,
    content::WebContents* web_contents,
    std::unique_ptr<AllPasswordsBottomSheetView> view,
    base::WeakPtr<password_manager::PasswordManagerDriver> driver,
    password_manager::PasswordStoreInterface* profile_store,
    password_manager::PasswordStoreInterface* account_store,
    base::OnceCallback<void()> dismissal_callback,
    FocusedFieldType focused_field_type,
    PasswordManagerClient* client,
    PasswordReuseDetectionManagerClient*
        password_reuse_detection_manager_client)
    : view_(std::move(view)),
      web_contents_(web_contents),
      profile_store_(profile_store),
      account_store_(account_store),
      dismissal_callback_(std::move(dismissal_callback)),
      driver_(std::move(driver)),
      focused_field_type_(focused_field_type),
      client_(client),
      password_reuse_detection_manager_client_(
          password_reuse_detection_manager_client) {}

AllPasswordsBottomSheetController::AllPasswordsBottomSheetController(
    content::WebContents* web_contents,
    password_manager::PasswordStoreInterface* profile_store,
    password_manager::PasswordStoreInterface* account_store,
    base::OnceCallback<void()> dismissal_callback,
    FocusedFieldType focused_field_type)
    : view_(std::make_unique<AllPasswordsBottomSheetViewImpl>(this)),
      web_contents_(web_contents),
      profile_store_(profile_store),
      account_store_(account_store),
      dismissal_callback_(std::move(dismissal_callback)),
      focused_field_type_(focused_field_type) {
  CHECK(web_contents_);
  CHECK(profile_store);
  CHECK(dismissal_callback_);
  auto* focused_frame = web_contents->GetFocusedFrame();
  CHECK(focused_frame->IsRenderFrameLive());
  password_manager::ContentPasswordManagerDriver* driver =
      password_manager::ContentPasswordManagerDriver::GetForRenderFrameHost(
          focused_frame);
  driver_ = driver->AsWeakPtr();
  client_ = ChromePasswordManagerClient::FromWebContents(web_contents_);
  password_reuse_detection_manager_client_ =
      ChromePasswordReuseDetectionManagerClient::FromWebContents(web_contents_);
}

AllPasswordsBottomSheetController::~AllPasswordsBottomSheetController() {
  if (authenticator_) {
    authenticator_->Cancel();
  }
}

void AllPasswordsBottomSheetController::Show() {
  if (on_password_forms_received_barrier_callback_) {
    return;
  }

  int awaiting_calls = account_store_ ? 2 : 1;
  on_password_forms_received_barrier_callback_ =
      base::BarrierCallback<std::vector<password_manager::PasswordForm>>(
          awaiting_calls,
          base::BindOnce(
              &AllPasswordsBottomSheetController::OnResultFromAllStoresReceived,
              weak_ptr_factory_.GetWeakPtr()));

  profile_store_->GetAllLoginsWithAffiliationAndBrandingInformation(
      weak_ptr_factory_.GetWeakPtr());
  if (account_store_) {
    account_store_->GetAllLoginsWithAffiliationAndBrandingInformation(
        weak_ptr_factory_.GetWeakPtr());
  }
}

void AllPasswordsBottomSheetController::OnGetPasswordStoreResultsOrErrorFrom(
    password_manager::PasswordStoreInterface* store,
    password_manager::LoginsResultOrError results_or_error) {
  CHECK(on_password_forms_received_barrier_callback_);
  if (std::holds_alternative<password_manager::PasswordStoreBackendError>(
          results_or_error)) {
    on_password_forms_received_barrier_callback_.Run({});
    return;
  }
  auto results =
      std::get<password_manager::LoginsResult>(std::move(results_or_error));
  std::erase_if(results, [](const auto& form) { return form.blocked_by_user; });

  on_password_forms_received_barrier_callback_.Run(
      password_manager::ToPasswordForms(std::move(results)));
}

Profile* AllPasswordsBottomSheetController::GetProfile() {
  return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
}

gfx::NativeView AllPasswordsBottomSheetController::GetNativeView() {
  return web_contents_->GetNativeView();
}

void AllPasswordsBottomSheetController::OnCredentialSelected(
    const std::u16string username,
    const std::u16string password,
    RequestsToFillPassword requests_to_fill_password) {
  const bool is_password_field =
      focused_field_type_ == FocusedFieldType::kFillablePasswordField;
  if (!driver_) {
    OnDismiss();
    return;
  }

  if (requests_to_fill_password && is_password_field) {
    // `client_` is guaranteed to be valid here.
    // Both the `client_` and `PasswordAccessoryController` are attached to
    // WebContents. And AllPasswordBottomSheetController is owned by
    // PasswordAccessoryController.
    DCHECK(client_);
    std::unique_ptr<device_reauth::DeviceAuthenticator> authenticator =
        client_->GetDeviceAuthenticator();
    if (client_->IsReauthBeforeFillingRequired(authenticator.get())) {
      authenticator_ = std::move(authenticator);
      authenticator_->AuthenticateWithMessage(
          u"",
          base::BindOnce(&AllPasswordsBottomSheetController::OnReauthCompleted,
                         base::Unretained(this), password));
      return;
    }

    FillPassword(password);
  } else if (!requests_to_fill_password) {
    driver_->FillIntoFocusedField(is_password_field, username);
  }

  // Consumes the dismissal callback to destroy the native controller and java
  // controller after the user selects a credential.
  OnDismiss();
}

void AllPasswordsBottomSheetController::OnDismiss() {
  std::move(dismissal_callback_).Run();
}

const GURL& AllPasswordsBottomSheetController::GetFrameUrl() {
  return driver_->GetLastCommittedURL();
}

void AllPasswordsBottomSheetController::OnReauthCompleted(
    const std::u16string& password,
    bool auth_succeeded) {
  authenticator_.reset();

  if (auth_succeeded) {
    FillPassword(password);
  }

  // Consumes the dismissal callback to destroy the native controller and java
  // controller after the user selects a credential.
  OnDismiss();
}

void AllPasswordsBottomSheetController::FillPassword(
    const std::u16string& password) {
  if (!driver_) {
    return;
  }
  driver_->FillIntoFocusedField(true, password);
  password_reuse_detection_manager_client_->OnPasswordSelected(password);
}

void AllPasswordsBottomSheetController::OnResultFromAllStoresReceived(
    std::vector<std::vector<password_manager::PasswordForm>> results) {
  CHECK(on_password_forms_received_barrier_callback_);
  CHECK(!results.empty());
  on_password_forms_received_barrier_callback_.Reset();

  if (results.size() > 1) {
    std::move(results[1].begin(), results[1].end(),
              std::back_inserter(results[0]));
  }
  view_->Show(std::move(results[0]), focused_field_type_);
}
