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

#import "base/strings/strcat.h"
#import "base/test/ios/wait_util.h"
#import "base/test/scoped_command_line.h"
#import "components/omnibox/browser/aim_eligibility_service_features.h"
#import "components/policy/core/common/policy_test_utils.h"
#import "components/policy/policy_constants.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/authentication/account_menu/public/account_menu_constants.h"
#import "ios/chrome/browser/authentication/test/signin_earl_grey.h"
#import "ios/chrome/browser/authentication/test/signin_earl_grey_ui_test_util.h"
#import "ios/chrome/browser/content_suggestions/public/ntp_home_constants.h"
#import "ios/chrome/browser/metrics/model/metrics_app_interface.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
#import "ios/chrome/browser/policy/model/policy_earl_grey_utils.h"
#import "ios/chrome/browser/popup_menu/public/popup_menu_constants.h"
#import "ios/chrome/browser/settings/manage_sync/public/manage_sync_settings_constants.h"
#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/signin/model/constants.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/signin/model/test_constants.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "net/test/embedded_test_server/embedded_test_server.h"
#import "net/test/embedded_test_server/http_request.h"
#import "net/test/embedded_test_server/http_response.h"
#import "ui/base/l10n/l10n_util.h"

namespace {

const char kPageLoadedString[] = "Page loaded!";
const char kPageURL[] = "/test-page.html";
const char kPageTitle[] = "Page title!";

// The passphrase for the fake sync server.
NSString* const kPassphrase = @"hello";

// The primary identity.
FakeSystemIdentity* const kPrimaryIdentity = [FakeSystemIdentity fakeIdentity1];

// Provides responses for redirect and changed window location URLs.
std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
    const net::test_server::HttpRequest& request) {
  if (request.relative_url != kPageURL) {
    return nullptr;
  }
  std::unique_ptr<net::test_server::BasicHttpResponse> http_response =
      std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_OK);
  http_response->set_content("<html><head><title>" + std::string(kPageTitle) +
                             "</title></head><body>" +
                             std::string(kPageLoadedString) + "</body></html>");
  return std::move(http_response);
}

// Pauses until the history label has disappeared.  History should not show on
// incognito.
BOOL WaitForHistoryToDisappear() {
  return [[GREYCondition
      conditionWithName:@"Wait for history to disappear"
                  block:^BOOL {
                    NSError* error = nil;
                    NSString* history =
                        l10n_util::GetNSString(IDS_HISTORY_SHOW_HISTORY);
                    [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(
                                                            history)]
                        assertWithMatcher:grey_notVisible()
                                    error:&error];
                    return error == nil;
                  }]
      waitWithTimeout:base::test::ios::kWaitForUIElementTimeout.InSecondsF()];
}

// The possible visibility states of quick actions.
enum class QuickActionsVisibility {
  // All three quick actions are fully visible.
  kVisible = 0,
  // Quick asctions are visible without incognito.
  kVisibleWithoutIncognito = 1,
  // Quick actions are not visible.
  kNotVisible = 2,
};

}  // namespace

@interface NewTabPageTestCase : ChromeTestCase
@property(nonatomic, assign) BOOL histogramTesterSet;
@end

@implementation NewTabPageTestCase

- (void)tearDownHelper {
  [self releaseHistogramTester];
  policy_test_utils::ClearPolicies();
  [super tearDownHelper];
}

- (void)setupHistogramTester {
  if (self.histogramTesterSet) {
    return;
  }
  self.histogramTesterSet = YES;
  chrome_test_util::GREYAssertErrorNil(
      [MetricsAppInterface setupHistogramTester]);
}

- (void)releaseHistogramTester {
  if (!self.histogramTesterSet) {
    return;
  }
  self.histogramTesterSet = NO;
  chrome_test_util::GREYAssertErrorNil(
      [MetricsAppInterface releaseHistogramTester]);
}

#pragma mark - Helpers

// Sets up the NTP Location policy dynamically at runtime.
- (void)setNTPPolicyValue:(std::string)ntpLocation {
  policy_test_utils::SetPolicyWithStringValue(ntpLocation,
                                              policy::key::kNewTabPageLocation);
}

// Validates that the new tab URL is the expected one.
- (void)validateNTPURL:(GURL)expectedURL {
  // Wait until the page has finished loading.
  [ChromeEarlGrey waitForPageToFinishLoading];

  // Validate the URL.
  const GURL currentURL = [ChromeEarlGrey webStateVisibleURL];
  GREYAssertEqual(expectedURL, currentURL, @"Page navigated unexpectedly to %s",
                  currentURL.spec().c_str());
}

#pragma mark - Tests

// Tests that all items are accessible on the most visited page.
- (void)testAccessibilityOnMostVisited {
  [ChromeEarlGrey verifyAccessibilityForCurrentScreen];
}

// Tests the metrics are reported correctly.
- (void)testNTPMetrics {
  self.testServer->RegisterRequestHandler(
      base::BindRepeating(&StandardResponse));
  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
  const GURL pageURL = self.testServer->GetURL(kPageURL);
  [ChromeEarlGrey closeAllTabs];

  // Open and close an NTP.
  [self setupHistogramTester];
  NSError* error =
      [MetricsAppInterface expectTotalCount:0
                               forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey closeAllTabs];
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"IOS.NTP.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"IOS.Home.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  [self releaseHistogramTester];

  // Open an incognito NTP and close it.
  [ChromeEarlGrey closeAllTabs];
  [self setupHistogramTester];
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey openNewIncognitoTab];
  [ChromeEarlGrey closeAllTabs];
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"IOS.NTP.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"IOS.Home.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  [self releaseHistogramTester];

  // Open an NTP and navigate to another URL.
  [self setupHistogramTester];
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:pageURL];
  [ChromeEarlGrey waitForWebStateContainingText:kPageLoadedString];

  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"IOS.NTP.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"IOS.Home.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  [self releaseHistogramTester];

  // Open an NTP and switch tab.
  [ChromeEarlGrey closeAllTabs];
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey loadURL:pageURL];
  [ChromeEarlGrey waitForWebStateContainingText:kPageLoadedString];

  [self setupHistogramTester];
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"IOS.NTP.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"IOS.Home.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey openNewTab];
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"IOS.NTP.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"IOS.Home.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey selectTabAtIndex:0];
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey selectTabAtIndex:1];
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey selectTabAtIndex:0];
  error = [MetricsAppInterface expectTotalCount:2
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  [self releaseHistogramTester];

  // Open two NTPs and close them.
  [ChromeEarlGrey closeAllTabs];
  [self setupHistogramTester];

  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"IOS.NTP.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:0
                                   forHistogram:@"IOS.Home.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey openNewTab];
  [ChromeEarlGrey openNewTab];
  error = [MetricsAppInterface expectTotalCount:1
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:2
                                   forHistogram:@"IOS.NTP.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:2
                                   forHistogram:@"IOS.Home.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  [ChromeEarlGrey closeAllTabs];
  error = [MetricsAppInterface expectTotalCount:2
                                   forHistogram:@"NewTabPage.TimeSpent"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:2
                                   forHistogram:@"IOS.NTP.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  error = [MetricsAppInterface expectTotalCount:2
                                   forHistogram:@"IOS.Home.Impression"];
  chrome_test_util::GREYAssertErrorNil(error);
  [self releaseHistogramTester];
}

// Tests that all items are accessible on the incognito page.
- (void)testAccessibilityOnIncognitoTab {
  [ChromeEarlGrey openNewIncognitoTab];
  GREYAssert(WaitForHistoryToDisappear(), @"History did not disappear.");
  [ChromeEarlGrey verifyAccessibilityForCurrentScreen];
  [ChromeEarlGrey closeAllIncognitoTabs];
}

// Tests that the Incognito NTP title has the 'Heading' trait for VoiceOver.
- (void)testIncognitoNTPHeadingTrait {
  [ChromeEarlGrey openNewIncognitoTab];
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::HeaderWithAccessibilityLabelId(
                                   IDS_NEW_TAB_OTR_TITLE)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [ChromeEarlGrey closeAllIncognitoTabs];
}

#pragma mark - Policy NTP Location Tests

// Tests that the new tab opens the policy's New Tab Page Location when the URL
// is valid.
- (void)testValidNTPLocation {
  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
  const GURL expectedURL = self.testServer->GetURL(kPageURL);

  // Set the policy's NTP Location value at runtime.
  [self setNTPPolicyValue:expectedURL.spec()];

  // Open a new tab page.
  [ChromeEarlGrey openNewTab];
  [self validateNTPURL:expectedURL];
}

// Tests that the new tab doesn't open the policy's New Tab Page Location when
// the URL is empty.
- (void)testEmptyNTPLocation {
  // Set the policy's NTP Location value at runtime.
  [self setNTPPolicyValue:""];

  // Open a new tab page.
  [ChromeEarlGrey openNewTab];

  // Verify that the new tab URL is chrome://newtab/.
  const GURL expectedURL(kChromeUINewTabURL);
  [self validateNTPURL:expectedURL];
}

// Tests that the incognito new tab doesn't open the policy's New Tab Page
// Location even if the URL is valid.
- (void)testIncognitoNTPLocation {
  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
  const GURL testURL = self.testServer->GetURL(kPageURL);

  // Set the policy's NTP Location value at runtime.
  [self setNTPPolicyValue:testURL.spec()];

  // Open a new incognito tab page.
  [ChromeEarlGrey openNewIncognitoTab];

  // Verify that the new tab URL is chrome://newtab/.
  const GURL expectedURL(kChromeUINewTabURL);
  [self validateNTPURL:expectedURL];

  [ChromeEarlGrey closeAllIncognitoTabs];
}

// Verifies that the app launches with a new tab page with the correct policy's
// New Tab Page Location URL.
- (void)testNewTabOnLaunchWithNTPLocation {
  // Close all existing tabs.
  [ChromeEarlGrey closeAllTabs];
  [ChromeEarlGrey closeAllIncognitoTabs];

  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
  const GURL expectedURL = self.testServer->GetURL(kPageURL);

  // Adds the NTP Location policy to the app's launch configuration.
  AppLaunchConfiguration config;
  config.additional_args.push_back("-NTPLocation");
  config.additional_args.push_back(expectedURL.spec());
  config.relaunch_policy = ForceRelaunchByCleanShutdown;
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  [self validateNTPURL:expectedURL];

  [ChromeEarlGrey removeUserDefaultsObjectForKey:@"NTPLocation"];
}

// Verifies opening a new tab from the New Tab button on the toolbar with the
// correct policy's New Tab Page Location URL.
// TODO(crbug.com/518881258): Flaky on simulator.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testNewTabByNewTabButtonTapWithNTPLocation \
  FLAKY_testNewTabByNewTabButtonTapWithNTPLocation
#else
#define MAYBE_testNewTabByNewTabButtonTapWithNTPLocation \
  testNewTabByNewTabButtonTapWithNTPLocation
#endif
- (void)MAYBE_testNewTabByNewTabButtonTapWithNTPLocation {
  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
  const GURL expectedURL = self.testServer->GetURL(kPageURL);

  // Set the policy's NTP Location value at runtime.
  [self setNTPPolicyValue:expectedURL.spec()];

  // Open tab via the UI.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::NewTabButton()]
      performAction:grey_tap()];

  [self validateNTPURL:expectedURL];
}

// Verifies opening a new tab from the tools menu with the correct policy's New
// Tab Page Location URL.
- (void)testNewTabFromToolsMenuWithNTPLocation {
  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
  const GURL expectedURL = self.testServer->GetURL(kPageURL);

  // Set the policy's NTP Location value at runtime.
  [self setNTPPolicyValue:expectedURL.spec()];

  // Open tab via the UI.
  [ChromeEarlGreyUI openToolsMenu];
  id<GREYMatcher> newTabButtonMatcher =
      grey_accessibilityID(kToolsMenuNewTabId);
  [[EarlGrey selectElementWithMatcher:newTabButtonMatcher]
      performAction:grey_tap()];

  [self validateNTPURL:expectedURL];
}

// Verifies opening a new tab by long pressing the tab grid view and selecting
// "New Tab" with the correct policy's New Tab Page Location URL.
- (void)testNewTabByLongPressTabGridViewWithNTPLocation {
  if ([ChromeEarlGrey isChromeNextEnabled] && [ChromeEarlGrey isIPadIdiom]) {
    EARL_GREY_TEST_DISABLED(@"The button doesn't exist with Next.");
  }
  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
  const GURL expectedURL = self.testServer->GetURL(kPageURL);

  // Set the policy's NTP Location value at runtime.
  [self setNTPPolicyValue:expectedURL.spec()];

  // Open a new incognito tab to expose the "New Tab" item in the long press
  // menu.
  [ChromeEarlGrey openNewIncognitoTab];

  // Open tab via the UI.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
      performAction:grey_longPress()];

  id<GREYMatcher> menuNewTabButtonMatcher;
  menuNewTabButtonMatcher =
      grey_allOf(chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
                     IDS_IOS_TOOLS_MENU_NEW_TAB),
                 grey_ancestor(grey_kindOfClassName(@"UICollectionView")), nil);
  [[EarlGrey selectElementWithMatcher:menuNewTabButtonMatcher]
      performAction:grey_tap()];

  [self validateNTPURL:expectedURL];

  [ChromeEarlGrey closeAllIncognitoTabs];
}

// Verifies opening a new tab from the tab grid view by tapping on the New Tab
// button with the correct policy's New Tab Page Location URL.
// TODO(crbug.com/518881258): Flaky on simulator.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testNewTabFromTabGridViewWithNTPLocation \
  FLAKY_testNewTabFromTabGridViewWithNTPLocation
#else
#define MAYBE_testNewTabFromTabGridViewWithNTPLocation \
  testNewTabFromTabGridViewWithNTPLocation
#endif
- (void)MAYBE_testNewTabFromTabGridViewWithNTPLocation {
  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
  const GURL expectedURL = self.testServer->GetURL(kPageURL);

  // Set the policy's NTP Location value at runtime.
  [self setNTPPolicyValue:expectedURL.spec()];

  // Open tab via the UI.
  [ChromeEarlGreyUI openTabGrid];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridNewTabButton()]
      performAction:grey_tap()];

  [self validateNTPURL:expectedURL];
}

// Tests that the error badge is shown on top of the identity disc when the
// primary account has a persistent error.
- (void)testErrorBadge {
  [SigninEarlGrey signinWithFakeIdentity:kPrimaryIdentity];
  [SigninEarlGrey
      setPersistentAuthErrorForAccount:CoreAccountId::FromGaiaId(
                                           kPrimaryIdentity.gaiaId)];
  //[ChromeEarlGrey addBookmarkWithSyncPassphrase:kPassphrase];
  [ChromeEarlGreyUI waitForAppToIdle];

  // Wait for the error badge to appear.
  id<GREYMatcher> errorBadgeMatcher =
      grey_accessibilityID(kNTPFeedHeaderIdentityDiscBadge);
  ConditionBlock condition = ^{
    NSError* error = nil;
    [[EarlGrey selectElementWithMatcher:errorBadgeMatcher]
        assertWithMatcher:grey_sufficientlyVisible()
                    error:&error];
    return !error;
  };
  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
                 base::test::ios::kWaitForUIElementTimeout, condition),
             @"Error badge didn't appear in the allotted time");

  // Tap on the identity disc to open the account menu.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kNTPFeedHeaderIdentityDisc)]
      performAction:grey_tap()];
  // Ensure the Account Menu is displayed.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kAccountMenuTableViewId)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Check the error button is displayed.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kAccountMenuErrorActionButtonId)]
      assertWithMatcher:grey_sufficientlyVisible()];
  // Tap on it.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kAccountMenuErrorActionButtonId)]
      performAction:grey_tap()];

  // Confirm the fake reauthentication dialog.
  [[EarlGrey
      selectElementWithMatcher:grey_allOf(
                                   grey_accessibilityID(
                                       kFakeAuthAddAccountButtonIdentifier),
                                   grey_sufficientlyVisible(), nil)]
      performAction:grey_tap()];

  // Verify the error badge on the ADP disappears.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kNTPFeedHeaderIdentityDiscBadge)]
      assertWithMatcher:grey_notVisible()];
}

- (AppLaunchConfiguration)appConfigurationForTestCase {
  AppLaunchConfiguration config = [super appConfigurationForTestCase];
  if ([self.name containsString:@"testPremiumAvatarRingWithAiTier"]) {
    config.features_enabled.push_back(kAiSubscriptionAvatarRingIOS);
    config.additional_args.push_back("--force-ai-subscription-tier=1");
  } else if ([self.name containsString:@"testPremiumAvatarRingWithoutAiTier"]) {
    config.features_enabled.push_back(kAiSubscriptionAvatarRingIOS);
    config.additional_args.push_back("--force-ai-subscription-tier=0");
  } else if ([self.name containsString:@"testPremiumAvatarRingWithError"]) {
    config.features_enabled.push_back(kAiSubscriptionAvatarRingIOS);
    config.additional_args.push_back("--force-ai-subscription-tier=1");
  } else if ([self.name containsString:
                            @"testPremiumAvatarRingWithDynamicTierChange"]) {
    config.features_enabled.push_back(kAiSubscriptionAvatarRingIOS);
  }
  return config;
}

// Tests that the premium avatar ring is displayed on the NTP when the user
// has an AI tier.
- (void)testPremiumAvatarRingWithAiTier {
  [ChromeEarlGrey openNewTab];
  // Sign in.
  [SigninEarlGrey signinWithFakeIdentity:kPrimaryIdentity];

  // Verify that the premium ring is visible.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kPremiumAvatarRingAccessibilityIdentifier)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that the premium avatar ring is NOT displayed on the NTP when the user
// does not have an AI tier.
- (void)testPremiumAvatarRingWithoutAiTier {
  [ChromeEarlGrey openNewTab];
  // Sign in.
  [SigninEarlGrey signinWithFakeIdentity:kPrimaryIdentity];

  // Verify that the premium ring is not visible.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kPremiumAvatarRingAccessibilityIdentifier)]
      assertWithMatcher:grey_notVisible()];
}

// Tests that the premium avatar ring is NOT displayed and the error badge is
// displayed when the user has an AI tier but also has an account error.
- (void)testPremiumAvatarRingWithError {
  [ChromeEarlGrey openNewTab];
  // Sign in.
  [SigninEarlGrey signinWithFakeIdentity:kPrimaryIdentity];

  // Verify that the premium ring is visible initially.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kPremiumAvatarRingAccessibilityIdentifier)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Trigger a persistent auth error.
  [SigninEarlGrey
      setPersistentAuthErrorForAccount:CoreAccountId::FromGaiaId(
                                           kPrimaryIdentity.gaiaId)];
  [ChromeEarlGreyUI waitForAppToIdle];

  // Verify that the error badge is visible.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kNTPFeedHeaderIdentityDiscBadge)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Verify that the premium ring is no longer visible.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kPremiumAvatarRingAccessibilityIdentifier)]
      assertWithMatcher:grey_notVisible()];
}

// Tests that the premium avatar ring appears and disappears dynamically when
// the AI subscription tier pref changes.
- (void)testPremiumAvatarRingWithDynamicTierChange {
  [ChromeEarlGrey openNewTab];
  // Ensure the pref is 0 initially.
  [ChromeEarlGrey setIntegerValue:0 forUserPref:"sync.ai_subscription_tier"];

  // Sign in.
  [SigninEarlGrey signinWithFakeIdentity:kPrimaryIdentity];

  // Verify that the premium ring is NOT visible initially.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kPremiumAvatarRingAccessibilityIdentifier)]
      assertWithMatcher:grey_notVisible()];

  // Change the pref to 1 (AI tier active).
  [ChromeEarlGrey setIntegerValue:1 forUserPref:"sync.ai_subscription_tier"];

  // Verify that the premium ring becomes visible.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kPremiumAvatarRingAccessibilityIdentifier)]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Change the pref back to 0.
  [ChromeEarlGrey setIntegerValue:0 forUserPref:"sync.ai_subscription_tier"];

  // Verify that the premium ring is no longer visible.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kPremiumAvatarRingAccessibilityIdentifier)]
      assertWithMatcher:grey_notVisible()];
}

@end
