// Copyright 2021 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/permissions/permission_update_message_controller_android.h"

#include "base/android/jni_android.h"
#include "base/memory/raw_ptr.h"
#include "base/test/mock_callback.h"
#include "chrome/browser/android/android_theme_resources.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/messages/android/mock_message_dispatcher_bridge.h"
#include "components/strings/grit/components_strings.h"

class PermissionUpdateMessageControllerAndroidTest
    : public ChromeRenderViewHostTestHarness {
 public:
  PermissionUpdateMessageControllerAndroidTest() = default;
  ~PermissionUpdateMessageControllerAndroidTest() override = default;

  void SetUp() override;
  void TearDown() override;

  PermissionUpdateMessageController* GetController() { return controller_; }

  void ShowLocation(base::OnceCallback<void(bool)> callback) {
    EXPECT_CALL(message_dispatcher_bridge_, EnqueueMessage);
    GetController()->ShowMessageInternal(
        {}, {}, {}, IDR_ANDROID_INFOBAR_GEOLOCATION,
        IDS_MESSAGE_MISSING_LOCATION_PERMISSION_TITLE,
        IDS_MESSAGE_MISSING_LOCATION_PERMISSION_TEXT, std::move(callback));
  }

  void ShowMedia(base::OnceCallback<void(bool)> callback) {
    EXPECT_CALL(message_dispatcher_bridge_, EnqueueMessage);
    GetController()->ShowMessageInternal(
        {}, {}, {}, IDR_ANDROID_INFOBAR_GEOLOCATION,
        IDS_MESSAGE_MISSING_MICROPHONE_CAMERA_PERMISSION_TITLE,
        IDS_MESSAGE_MISSING_MICROPHONE_CAMERA_PERMISSIONS_TEXT,
        std::move(callback));
  }

  void ShowLocalNetwork(base::OnceCallback<void(bool)> callback) {
    EXPECT_CALL(message_dispatcher_bridge_, EnqueueMessage);
    GetController()->ShowMessageInternal(
        {}, {}, {}, IDR_ANDROID_INFOBAR_LOCAL_NETWORK,
        IDS_MESSAGE_MISSING_LOCAL_NETWORK_PERMISSION_TITLE,
        IDS_MESSAGE_MISSING_LOCAL_NETWORK_PERMISSION_TEXT, std::move(callback));
  }

  size_t GetMessageDelegatesSize() {
    return GetController()->message_delegates_.size();
  }

  void OnPermissionGranted(bool granted) {
    GetController()->message_delegates_.front()->OnPermissionResult(granted);
  }

  void SetPermissionResultTriggeredSynchronously() {
    GetController()->message_delegates_.front()->message_->SetActionClick(
        base::BindOnce(
            &PermissionUpdateMessageControllerAndroidTest::OnPermissionGranted,
            base::Unretained(this), false));
  }

  void DismissedByPrimaryAction() {
    messages::MessageDispatcherBridge::Get()->DismissMessage(
        GetController()->message_delegates_.front()->message_.get(),
        messages::DismissReason::PRIMARY_ACTION);
  }

  void ExpectDismiss(messages::DismissReason expected_reason) {
    EXPECT_CALL(message_dispatcher_bridge_, DismissMessage)
        .WillOnce([expected_reason](messages::MessageWrapper* message,
                                    messages::DismissReason dismiss_reason) {
          EXPECT_EQ(static_cast<int>(expected_reason),
                    static_cast<int>(dismiss_reason));
          message->HandleDismissCallback(base::android::AttachCurrentThread(),
                                         static_cast<int>(dismiss_reason));
        });
  }

 private:
  raw_ptr<PermissionUpdateMessageController> controller_;
  messages::MockMessageDispatcherBridge message_dispatcher_bridge_;
};

void PermissionUpdateMessageControllerAndroidTest::SetUp() {
  ChromeRenderViewHostTestHarness::SetUp();
  messages::MessageDispatcherBridge::SetInstanceForTesting(
      &message_dispatcher_bridge_);
  PermissionUpdateMessageController::CreateForWebContents(web_contents());
  controller_ =
      PermissionUpdateMessageController::FromWebContents(web_contents());
}

void PermissionUpdateMessageControllerAndroidTest::TearDown() {
  messages::MessageDispatcherBridge::SetInstanceForTesting(nullptr);
  ChromeRenderViewHostTestHarness::TearDown();
}

TEST_F(PermissionUpdateMessageControllerAndroidTest,
       OnMessageDismissedByPrimaryAction) {
  base::MockOnceCallback<void(bool)> mock_permission_update_callback1;
  base::MockOnceCallback<void(bool)> mock_permission_update_callback2;
  ShowLocation(mock_permission_update_callback1.Get());
  EXPECT_EQ(1u, GetMessageDelegatesSize());
  ShowMedia(mock_permission_update_callback2.Get());
  EXPECT_EQ(2u, GetMessageDelegatesSize());
  EXPECT_CALL(mock_permission_update_callback1, Run(false));

  // Message is dismissed first by primary action, and then permission update
  // callback is invoked. In this case, the dismiss reason should be
  // PRIMARY_ACTION.
  ExpectDismiss(messages::DismissReason::PRIMARY_ACTION);
  DismissedByPrimaryAction();
  OnPermissionGranted(false);
  EXPECT_EQ(1u, GetMessageDelegatesSize());
  EXPECT_CALL(mock_permission_update_callback2, Run(true));
  ExpectDismiss(messages::DismissReason::PRIMARY_ACTION);
  DismissedByPrimaryAction();
  OnPermissionGranted(true);
  EXPECT_EQ(0u, GetMessageDelegatesSize());
}

TEST_F(PermissionUpdateMessageControllerAndroidTest, OnPermissionResult) {
  // Permission update callback is invoked before dismiss callback is triggered.
  // In this case, dismiss callback is invoked when the controller is trying
  // to delete the message delegate. The dismiss reason should be UNKNOWN.
  base::MockOnceCallback<void(bool)> mock_permission_update_callback1;
  ShowLocation(mock_permission_update_callback1.Get());
  EXPECT_EQ(1u, GetMessageDelegatesSize());
  SetPermissionResultTriggeredSynchronously();
  EXPECT_CALL(mock_permission_update_callback1, Run(true));
  ExpectDismiss(messages::DismissReason::UNKNOWN);
  OnPermissionGranted(true);
  EXPECT_EQ(0u, GetMessageDelegatesSize());

  base::MockOnceCallback<void(bool)> mock_permission_update_callback2;
  ShowMedia(mock_permission_update_callback2.Get());
  EXPECT_EQ(1u, GetMessageDelegatesSize());
  SetPermissionResultTriggeredSynchronously();
  EXPECT_CALL(mock_permission_update_callback2, Run(false));
  ExpectDismiss(messages::DismissReason::UNKNOWN);
  OnPermissionGranted(false);
  EXPECT_EQ(0u, GetMessageDelegatesSize());
}

// Tests that Local Network Access permission missing reprompt messages are
// correctly enqueued with IDR_ANDROID_INFOBAR_LOCAL_NETWORK (router icon
// R.drawable.router_24), missing title, and missing text string resource IDs.
TEST_F(PermissionUpdateMessageControllerAndroidTest,
       ShowLocalNetworkPermission) {
  base::MockOnceCallback<void(bool)> mock_permission_update_callback;
  ShowLocalNetwork(mock_permission_update_callback.Get());
  EXPECT_EQ(1u, GetMessageDelegatesSize());
  EXPECT_CALL(mock_permission_update_callback, Run(true));

  ExpectDismiss(messages::DismissReason::PRIMARY_ACTION);
  DismissedByPrimaryAction();
  OnPermissionGranted(true);
  EXPECT_EQ(0u, GetMessageDelegatesSize());
}
