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

#import "components/feature_engagement/public/feature_constants.h"
#import "components/signin/internal/identity_manager/account_capabilities_constants.h"
#import "components/signin/public/base/signin_metrics.h"
#import "ios/chrome/browser/authentication/account_menu/public/account_menu_constants.h"
#import "ios/chrome/browser/authentication/test/expected_signin_histograms.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/authentication/test/signin_matchers.h"
#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
#import "ios/chrome/browser/metrics/model/metrics_app_interface.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/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/whats_new/public/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/chrome/test/earl_grey/test_switches.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ui/base/l10n/l10n_util.h"

namespace {

NSString* const kPassphrase = @"hello";

// Get the accessibility ID of a destination with an error badge.
NSString* GetDestinationIdWithErrorBadge(NSString* identifier) {
  return [@[ identifier, @"errorBadge" ] componentsJoinedByString:@"-"];
}

// Get the accessibility ID of a destination with a promo badge.
NSString* GetDestinationIdWithPromoBadge(NSString* identifier) {
  return [@[ identifier, @"promoBadge" ] componentsJoinedByString:@"-"];
}

// Get the accessibility ID of a destination with a "New" badge.
NSString* GetDestinationIdWithNewBadge(NSString* identifier) {
  return [@[ identifier, @"newBadge" ] componentsJoinedByString:@"-"];
}

// Get the matcher for the Settings destination with a promo badge.
id<GREYMatcher> GetSettingsDestinationWithPromoBadgeMatcher() {
  return grey_accessibilityID(
      GetDestinationIdWithPromoBadge(kToolsMenuSettingsId));
}

// Get the matcher for the Settings destination with an error badge.
id<GREYMatcher> GetSettingsDestinationWithErrorBadgeMatcher() {
  return grey_accessibilityID(
      GetDestinationIdWithErrorBadge(kToolsMenuSettingsId));
}

// Get the matcher for the What's New destination with a "New" badge.
id<GREYMatcher> GetWhatsNewDestinationWithNewBadgeMatcher() {
  return grey_accessibilityID(
      GetDestinationIdWithNewBadge(kToolsMenuWhatsNewId));
}

// Cleans up the data related to the destination badge highlight features, e.g.,
// to highlight What's New with a badge.
void CleanupDestinationsHighlightFeaturesData() {
  // Clean up Overflow Menu usage history ranking data.
  [ChromeEarlGrey
      resetDataForLocalStatePref:prefs::kOverflowMenuDestinationUsageHistory];
  [ChromeEarlGrey
      resetDataForLocalStatePref:prefs::kOverflowMenuNewDestinations];

  // Clean up What's New destination promo data.
  [ChromeEarlGrey removeUserDefaultsObjectForKey:kWhatsNewM116UsageEntryKey];
}

// Resolves the passphrase error from the Overflow Menu.
void ResolvePassphraseErrorFromOverflowMenu() {
  // Tap on the Settings destination that has an error badge.
  [ChromeEarlGreyUI
      tapToolsMenuButton:GetSettingsDestinationWithErrorBadgeMatcher()];

  // Enter passphrase to resolve the identity error.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsAccountButton()]
      performAction:grey_tap()];
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kSyncErrorButtonIdentifier)]
      performAction:grey_tap()];
  [SigninEarlGreyUI submitSyncPassphrase:kPassphrase];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
      performAction:grey_tap()];
}

}  // namespace

// Tests for the Overflow Menu carousel features that don't directly touch the
// destination usage history ranking API.
@interface OverflowMenuTestCase : ChromeTestCase
@end

@implementation OverflowMenuTestCase

#pragma mark - ChromeTestCase overrides

- (void)setUp {
  [super setUp];

  CleanupDestinationsHighlightFeaturesData();
}

- (void)tearDownHelper {
  // Clean up sign-in and Sync data.
  [SigninEarlGrey signOut];
  [ChromeEarlGrey clearFakeSyncServerData];

  CleanupDestinationsHighlightFeaturesData();

  [super tearDownHelper];
}

#pragma mark - Tests

// Tests, in Butter mode, that the Settings destination is highlighted with an
// error badge when there is an identity error, and that the error badge is
// dismissed when the error is resolved.
//
// Emulates a passphrase error in the signed in account to trigger the identity
// error indicators (in butter mode, no Sync).
- (void)testSettingsDestinationWithIdentityErrorInButterMode {
  // Encrypt synced data with a passphrase to enable passphrase encryption for
  // the signed in account.
  [ChromeEarlGrey addSyncPassphrase:kPassphrase];

  // Sign in in butter mode while keeping sync disabled.
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey signinWithFakeIdentity:fakeIdentity];

  // Verify that the error badge is shown.
  [ChromeEarlGreyUI openToolsMenu];
  [[EarlGrey
      selectElementWithMatcher:GetSettingsDestinationWithErrorBadgeMatcher()]
      assertWithMatcher:grey_notNil()];

  ResolvePassphraseErrorFromOverflowMenu();

  // Verify that the promo badge is shown once the identity error is resolved
  // and the error badge dismissed.
  [ChromeEarlGreyUI openToolsMenu];
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::SettingsDestinationButton()]
      assertWithMatcher:grey_notNil()];
}

// Tests that the Sync error is indicated on the Settings destination (without
// Butter).
//
// Emulates a passphrase error in the synced account to trigger the error
// indicator.
- (void)testSettingsDestinationIdentityErrorBadgeWithSync {
  // Encrypt synced data with a passphrase to enable passphrase encryption for
  // the signed in account.
  [ChromeEarlGrey addSyncPassphrase:kPassphrase];

  // Sign in and Sync account.
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey signinWithFakeIdentity:fakeIdentity];

  // Verifies that the error badge is shown.
  [ChromeEarlGreyUI openToolsMenu];
  [[EarlGrey
      selectElementWithMatcher:GetSettingsDestinationWithErrorBadgeMatcher()]
      assertWithMatcher:grey_notNil()];
}

// Tests non-error destination highlights.
// TODO(crbug.com/40263342): This test is very flaky. Fails especially on
// devices.
- (void)FLAKY_testNonErrorDestinationHighlights {
  AppLaunchConfiguration config;
  // Enable Overflow Menu destinations highlight features.
  config.additional_args.push_back(
      "--enable-features=IPH_DemoMode:chosen_feature"
      "/IPH_iOSDefaultBrowserOverflowMenuBadge");
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Wait to make sure that the page had the time to load after the browser
  // loaded. The test would be flaky otherwise.
  [ChromeEarlGreyUI waitForToolbarVisible:YES];

  [ChromeEarlGreyUI openToolsMenu];

  // Verifies that both destinations are there with the right badge.
  [[EarlGrey
      selectElementWithMatcher:GetSettingsDestinationWithPromoBadgeMatcher()]
      assertWithMatcher:grey_notNil()];
  [[EarlGrey
      selectElementWithMatcher:GetWhatsNewDestinationWithNewBadgeMatcher()]
      assertWithMatcher:grey_notNil()];
}

// Tests that the overflow menu footer displays Family Link disclaimer with a
// link to more information about family accounts.
// TODO(crbug.com/339689514): Failing on ios-simulator-noncq
- (void)DISABLED_testOverflowMenuFooterFamilyLink {
  // Sign in and Sync account.
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey addFakeIdentity:fakeIdentity
                 withCapabilities:@{
                   @(kIsSubjectToParentalControlsCapabilityName) : @YES,
                 }];
  [SigninEarlGrey signinWithFakeIdentity:fakeIdentity];

  // Open tools menu to click on "Learn more" family link footer.
  [ChromeEarlGreyUI openToolsMenu];
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kPopupMenuToolsMenuActionListId)]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
  [ChromeEarlGreyUI
      tapToolsMenuAction:grey_accessibilityID(kTextMenuFamilyLinkInfo)];

  // Wait for the Family Link page to be visible.
  [ChromeEarlGrey waitForWebStateVisible];
}

- (void)testOverflowMenuCustomizationIPH {
  if ([ChromeEarlGrey isOverflowMenuNTPRefactorEnabled]) {
    EARL_GREY_TEST_SKIPPED(
        @"Customization IPH is disabled on NTP when OverflowMenuNTPRefactor is "
        @"enabled.");
  }

  AppLaunchConfiguration config;
  config.iph_feature_enabled =
      feature_engagement::kIPHiOSOverflowMenuCustomizationFeature.name;
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Open tools menu and see IPH appears.
  [ChromeEarlGreyUI openToolsMenu];
  [ChromeEarlGrey
      waitForUIElementToAppearWithMatcher:grey_accessibilityID(
                                              @"BubbleViewLabelIdentifier")];
}


// Tests that tapping the identity button (when signed in) opens the Account
// Menu and records the correct user action and access point UMA metric.
- (void)testAccountMenuFromOverflowMenu {
  AppLaunchConfiguration config;
  config.additional_args.push_back("--enable-features=IdentityAwareness");
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey signinWithFakeIdentity:fakeIdentity];

  chrome_test_util::GREYAssertErrorNil(
      [MetricsAppInterface setupHistogramTester]);

  [ChromeEarlGreyUI openToolsMenu];

  // Verify the identity button is visible.
  id<GREYMatcher> identityButton = grey_accessibilityID(kToolsMenuIdentityId);
  [[EarlGrey selectElementWithMatcher:identityButton]
      assertWithMatcher:grey_sufficientlyVisible()];

  GREYAssertNil([MetricsAppInterface setupUserActionTester],
                @"Cannot setup user action tester.");

  // Tap on the identity button in the overflow menu.
  [[EarlGrey selectElementWithMatcher:identityButton] performAction:grey_tap()];

  GREYAssertNil([MetricsAppInterface expectCount:1
                                   forUserAction:@"MobileMenuIdentityMenu"],
                @"MobileMenuIdentityMenu user action was not recorded.");

  GREYAssertNil([MetricsAppInterface releaseUserActionTester],
                @"Cannot release user action tester.");

  // Verify the Account Menu is displayed.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kAccountMenuTableViewId)]
      assertWithMatcher:grey_sufficientlyVisible()];

  chrome_test_util::GREYAssertErrorNil([MetricsAppInterface
      expectUniqueSampleWithCount:1
                        forBucket:static_cast<int>(
                                      AccountMenuAccessPoint::kOverflowMenu)
                     forHistogram:@"Signin.IOSAccountMenu.Opened"]);

  chrome_test_util::GREYAssertErrorNil(
      [MetricsAppInterface releaseHistogramTester]);
}

@end
