// 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/infobars/model/overlays/translate_overlay_tab_helper.h"

#import "base/memory/raw_ptr.h"
#import "ios/chrome/browser/infobars/model/infobar_ios.h"
#import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
#import "ios/chrome/browser/infobars/model/overlays/default_infobar_overlay_request_factory.h"
#import "ios/chrome/browser/infobars/model/overlays/infobar_overlay_request_inserter.h"
#import "ios/chrome/browser/infobars/model/overlays/infobar_overlay_util.h"
#import "ios/chrome/browser/infobars/model/test/fake_infobar_ios.h"
#import "ios/chrome/browser/overlays/model/public/default/default_infobar_overlay_request_config.h"
#import "ios/chrome/browser/overlays/model/public/infobar_banner/infobar_banner_placeholder_request_config.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_request_queue_util.h"
#import "ios/chrome/browser/passwords/infobars/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/tips_manager/model/tips_manager_ios_factory.h"
#import "ios/chrome/browser/translate/model/fake_translate_infobar_delegate.h"
#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
#import "ios/web/public/test/fakes/fake_navigation_manager.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/platform_test.h"

namespace {
DEFINE_STATELESS_OVERLAY_REQUEST_CONFIG(FakeConfig);

// Creates a matcher callback for ConfigType and config's InfoBar.
template <class ConfigType>
base::RepeatingCallback<bool(OverlayRequest*)> ConfigAndInfoBarMatcher(
    InfoBarIOS* infobar) {
  return base::BindRepeating(
      [](InfoBarIOS* infobar, OverlayRequest* request) -> bool {
        return GetOverlayRequestInfobar(request) == infobar &&
               request->GetConfig<ConfigType>();
      },
      infobar);
}
}  // namespace

// Test fixture for TranslateInfobarOverlayTranslateOverlayTabHelper.
class TranslateInfobarOverlayTranslateOverlayTabHelperTest
    : public PlatformTest {
 public:
  TranslateInfobarOverlayTranslateOverlayTabHelperTest() {
    TestProfileIOS::Builder test_profile_builder;

    test_profile_builder.AddTestingFactory(
        TipsManagerIOSFactory::GetInstance(),
        TipsManagerIOSFactory::GetDefaultFactory());

    profile_ = std::move(test_profile_builder).Build();

    // Set up WebState
    web_state_.SetNavigationManager(
        std::make_unique<web::FakeNavigationManager>());

    // Associate the WebState with the Profile
    web_state_.SetBrowserState(profile_.get());

    OverlayRequestQueue::CreateForWebState(&web_state_);
    InfoBarManagerImpl::CreateForWebState(&web_state_);
    InfobarOverlayRequestInserter::CreateForWebState(
        &web_state_, &DefaultInfobarOverlayRequestFactory);
    TranslateOverlayTabHelper::CreateForWebState(&web_state_);

    std::unique_ptr<FakeTranslateInfoBarDelegate> delegate =
        delegate_factory_.CreateFakeTranslateInfoBarDelegate("fr", "en");
    delegate_ = delegate.get();
    std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
        InfobarType::kInfobarTypeTranslate, std::move(delegate));
    infobar_ = infobar.get();
    InfoBarManagerImpl::FromWebState(&web_state_)
        ->AddInfoBar(std::move(infobar));
  }

  ~TranslateInfobarOverlayTranslateOverlayTabHelperTest() override = default;

  // Returns the front request of `web_state_`'s OverlayRequestQueue.
  OverlayRequest* front_request() {
    return OverlayRequestQueue::FromWebState(&web_state_,
                                             OverlayModality::kInfobarBanner)
        ->front_request();
  }

 protected:
  web::WebTaskEnvironment task_environment_;
  IOSChromeScopedTestingLocalState scoped_testing_local_state_;
  std::unique_ptr<TestProfileIOS> profile_;
  web::FakeWebState web_state_;
  FakeTranslateInfoBarDelegateFactory delegate_factory_;
  raw_ptr<FakeTranslateInfoBarDelegate, DanglingUntriaged> delegate_ = nullptr;
  raw_ptr<InfoBarIOS, DanglingUntriaged> infobar_ = nullptr;
};

// Tests that the inserter adds a placeholder request when Translate begins.
TEST_F(TranslateInfobarOverlayTranslateOverlayTabHelperTest,
       InsertPlaceholderDuringTranslate) {
  InsertParams params(infobar_);
  params.overlay_type = InfobarOverlayType::kBanner;
  params.insertion_index = 0;
  params.source = InfobarOverlayInsertionSource::kInfoBarManager;
  InfobarOverlayRequestInserter::FromWebState(&web_state_)
      ->InsertOverlayRequest(params);

  delegate_->TriggerOnTranslateStepChanged(
      translate::TRANSLATE_STEP_TRANSLATING, translate::TranslateErrors::NONE);
  OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
      &web_state_, OverlayModality::kInfobarBanner);
  EXPECT_EQ(2U, queue->size());
  EXPECT_TRUE(
      queue->GetRequest(1)->GetConfig<InfobarBannerPlaceholderRequestConfig>());
}

// Tests that a placeholder is not added to the queue if there is no existing
// banner request in the queue (e.g. auto-translate is on or Tranlsate is
// triggered from the modal).
TEST_F(TranslateInfobarOverlayTranslateOverlayTabHelperTest,
       NoPlaceholderIfNoBannerDuringTranslate) {
  OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
      &web_state_, OverlayModality::kInfobarBanner);

  delegate_->TriggerOnTranslateStepChanged(
      translate::TRANSLATE_STEP_TRANSLATING, translate::TranslateErrors::NONE);
  EXPECT_EQ(0U, queue->size());
}

// Tests that the second Translate banner is inserted behind an existing
// placeholder request when Translate finishes.
TEST_F(TranslateInfobarOverlayTranslateOverlayTabHelperTest,
       InsertBehindPlaceholderAfterTranslateFinishes) {
  OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
      &web_state_, OverlayModality::kInfobarBanner);
  std::unique_ptr<OverlayRequest> request =
      OverlayRequest::CreateWithConfig<InfobarBannerPlaceholderRequestConfig>(
          infobar_);
  queue->AddRequest(std::move(request), nullptr);
  std::unique_ptr<OverlayRequest> second_request =
      OverlayRequest::CreateWithConfig<FakeConfig>();
  queue->AddRequest(std::move(second_request), nullptr);

  delegate_->TriggerOnTranslateStepChanged(
      translate::TRANSLATE_STEP_AFTER_TRANSLATE,
      translate::TranslateErrors::NONE);

  size_t second_banner_index = 0;
  EXPECT_TRUE(GetIndexOfMatchingRequest(
      queue, &second_banner_index,
      ConfigAndInfoBarMatcher<DefaultInfobarOverlayRequestConfig>(infobar_)));
  EXPECT_EQ(1U, second_banner_index);
}

// Tests that the inserter adds a banner request to the end of the queue after
// Translate finishes and there is no placeholder in the queue.
TEST_F(TranslateInfobarOverlayTranslateOverlayTabHelperTest,
       InsertWithoutPlaceholderAfterTranslateFinishes) {
  OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
      &web_state_, OverlayModality::kInfobarBanner);
  queue->AddRequest(OverlayRequest::CreateWithConfig<FakeConfig>());

  delegate_->TriggerOnTranslateStepChanged(
      translate::TRANSLATE_STEP_AFTER_TRANSLATE,
      translate::TranslateErrors::NONE);

  EXPECT_EQ(2U, queue->size());
  EXPECT_TRUE(
      queue->GetRequest(1)->GetConfig<DefaultInfobarOverlayRequestConfig>());
}
