// 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.

#import "ios/chrome/browser/overlays/model/public/overlay_browser_agent_base.h"

#import "base/memory/raw_ptr.h"
#import "ios/chrome/browser/overlays/model/public/overlay_callback_manager.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request_callback_installer.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request_config.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request_queue.h"
#import "ios/chrome/browser/overlays/model/public/overlay_response.h"
#import "ios/chrome/browser/overlays/model/public/overlay_response_info.h"
#import "ios/chrome/browser/overlays/model/test/fake_overlay_presentation_context.h"
#import "ios/chrome/browser/overlays/model/test/fake_overlay_request_callback_installer.h"
#import "ios/chrome/browser/shared/model/browser/browser_user_data.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/platform_test.h"

namespace {
// The modality to use in tests.
const OverlayModality kModality = OverlayModality::kWebContentArea;
// Request configs used in tests.
DEFINE_STATELESS_OVERLAY_REQUEST_CONFIG(SupportedConfig);
DEFINE_STATELESS_OVERLAY_REQUEST_CONFIG(UnsupportedConfig);
DEFINE_STATELESS_OVERLAY_RESPONSE_INFO(DispatchInfo);

// Fake version of OverlayBrowserAgentBase used for tests.
class FakeOverlayBrowserAgent
    : public OverlayBrowserAgentBase,
      public BrowserUserData<FakeOverlayBrowserAgent> {
 public:
  // The mock callback receiver used by the FakeOverlayRequestCallbackInstaller
  // whose callback installation is driven by the OverlayBrowserAgentBase
  // superclass.
  MockOverlayRequestCallbackReceiver& mock_callback_receiver() {
    return mock_callback_receiver_;
  }

 private:
  friend class BrowserUserData<FakeOverlayBrowserAgent>;

  FakeOverlayBrowserAgent(Browser* browser)
      : OverlayBrowserAgentBase(browser), BrowserUserData(browser) {
    // Add a fake callback installer for kModality that supports requests
    // configured with SupportedConfig and dispatched responses with
    // DispatchInfo.
    std::unique_ptr<FakeOverlayRequestCallbackInstaller> installer =
        std::make_unique<FakeOverlayRequestCallbackInstaller>(
            &mock_callback_receiver_, std::set<const OverlayResponseSupport*>(
                                          {DispatchInfo::ResponseSupport()}));
    installer->set_request_support(SupportedConfig::RequestSupport());
    AddInstaller(std::move(installer), kModality);
  }

  testing::StrictMock<MockOverlayRequestCallbackReceiver>
      mock_callback_receiver_;
};
}  // namespace

// Test fixture for OverlayBrowserAgentBase.
class OverlayBrowserAgentBaseTest : public PlatformTest {
 public:
  OverlayBrowserAgentBaseTest() {
    // Create the Browser and set up the browser agent.
    TestProfileIOS::Builder builder;
    profile_ = std::move(builder).Build();
    browser_ = std::make_unique<TestBrowser>(profile_.get());
    FakeOverlayBrowserAgent::CreateForBrowser(browser_.get());
    // Set up the infobar OverlayPresenter.
    overlay_presenter_ =
        OverlayPresenter::FromBrowser(browser_.get(), kModality);
    overlay_presenter_->SetPresentationContext(&presentation_context_);
    // Add and active a WebState over which to present overlays.
    browser_->GetWebStateList()->InsertWebState(
        std::make_unique<web::FakeWebState>(),
        WebStateList::InsertionParams::Automatic().Activate());
    web_state_ = browser_->GetWebStateList()->GetActiveWebState();
  }

  ~OverlayBrowserAgentBaseTest() override {
    overlay_presenter_->SetPresentationContext(nullptr);
  }

  // Returns the mock callback receiver for the browser agent.
  MockOverlayRequestCallbackReceiver& mock_callback_receiver() {
    return FakeOverlayBrowserAgent::FromBrowser(browser_.get())
        ->mock_callback_receiver();
  }

  // Returns `web_state_`'s request queue.
  OverlayRequestQueue* queue() {
    return OverlayRequestQueue::FromWebState(web_state_, kModality);
  }

  // Cancels all requests in `web_state_`'s queue.
  void CancelRequests() { queue()->CancelAllRequests(); }

 protected:
  web::WebTaskEnvironment task_environment_;
  std::unique_ptr<ProfileIOS> profile_;
  std::unique_ptr<Browser> browser_;
  raw_ptr<OverlayPresenter> overlay_presenter_ = nullptr;
  raw_ptr<web::WebState, DanglingUntriaged> web_state_ = nullptr;
  FakeOverlayPresentationContext presentation_context_;
};

// Tests that callbacks are successfully set up for supported requests.
TEST_F(OverlayBrowserAgentBaseTest, SupportedRequestCallbackSetup) {
  // Add a supported request to the queue so that its presentation is simulated
  // in the fake presentation context, triggering the BrowserAgent to install
  // its callbacks on the request.
  std::unique_ptr<OverlayRequest> added_request =
      OverlayRequest::CreateWithConfig<SupportedConfig>();
  OverlayRequest* request = added_request.get();
  queue()->AddRequest(std::move(added_request));

  // Dispatch a response through this presented request, expecting the dispatch
  // callback to be executed on the mock receiver.
  EXPECT_CALL(mock_callback_receiver(),
              DispatchCallback(request, DispatchInfo::ResponseSupport()));
  request->GetCallbackManager()->DispatchResponse(
      OverlayResponse::CreateWithInfo<DispatchInfo>());

  // Cancel the request, expecting the completion callback to be executed on the
  // mock receiver.
  EXPECT_CALL(mock_callback_receiver(), CompletionCallback(request));
  CancelRequests();
}

// Tests that callbacks are not executed for supported requests.
TEST_F(OverlayBrowserAgentBaseTest, UnsupportedRequestCallbackSetup) {
  // Add an unsupported request to the queue so that its presentation is
  // simulated in the fake presentation context.  Since the added request is
  // unsupported, no callbacks should have been installed.
  std::unique_ptr<OverlayRequest> added_request =
      OverlayRequest::CreateWithConfig<UnsupportedConfig>();
  OverlayRequest* request = added_request.get();
  queue()->AddRequest(std::move(added_request));

  // Dispatch a response through this presented request without expecting the
  // dispatch callback to be executed on the mock receiver.
  std::unique_ptr<OverlayResponse> response =
      OverlayResponse::CreateWithInfo<DispatchInfo>();
  request->GetCallbackManager()->DispatchResponse(std::move(response));

  // Cancel the request without expecting the completion callback to be executed
  // on the mock receiver.
  CancelRequests();
}
