// Copyright 2015 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/settings/ui_bundled/privacy/privacy_table_view_controller.h"

#import <LocalAuthentication/LocalAuthentication.h>

#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "base/memory/raw_ptr.h"
#import "base/metrics/histogram_macros.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "base/strings/sys_string_conversions.h"
#import "components/content_settings/core/common/features.h"
#import "components/feature_engagement/public/event_constants.h"
#import "components/feature_engagement/public/feature_constants.h"
#import "components/feature_engagement/public/feature_list.h"
#import "components/feature_engagement/public/tracker.h"
#import "components/handoff/pref_names_ios.h"
#import "components/password_manager/core/common/password_manager_pref_names.h"
#import "components/prefs/ios/pref_observer_bridge.h"
#import "components/prefs/pref_change_registrar.h"
#import "components/prefs/pref_service.h"
#import "components/safe_browsing/core/common/features.h"
#import "components/safe_browsing/core/common/safe_browsing_prefs.h"
#import "components/signin/public/identity_manager/account_info.h"
#import "components/strings/grit/components_strings.h"
#import "components/sync/service/sync_service.h"
#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
#import "ios/chrome/browser/incognito_interstitial/ui_bundled/incognito_interstitial_constants.h"
#import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_util.h"
#import "ios/chrome/browser/net/model/crurl.h"
#import "ios/chrome/browser/policy/model/policy_util.h"
#import "ios/chrome/browser/settings/ui_bundled/elements/enterprise_info_popover_view_controller.h"
#import "ios/chrome/browser/settings/ui_bundled/elements/info_popover_view_controller.h"
#import "ios/chrome/browser/settings/ui_bundled/elements/supervised_user_info_popover_view_controller.h"
#import "ios/chrome/browser/settings/ui_bundled/privacy/privacy_constants.h"
#import "ios/chrome/browser/settings/ui_bundled/privacy/privacy_navigation_commands.h"
#import "ios/chrome/browser/settings/ui_bundled/privacy/safe_browsing/safe_browsing_constants.h"
#import "ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.h"
#import "ios/chrome/browser/settings/ui_bundled/settings_table_view_controller_constants.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser/browser_observer_bridge.h"
#import "ios/chrome/browser/shared/model/prefs/pref_backed_boolean.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/public/commands/open_new_tab_command.h"
#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/list_model/list_model.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_info_button_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_switch_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_capabilities.h"
#import "ios/chrome/browser/sync/model/sync_observer_bridge.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/common/string_util.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/reauthentication/reauthentication_protocol.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/components/security_interstitials/https_only_mode/feature.h"
#import "ui/base/l10n/l10n_util.h"
#import "ui/base/l10n/l10n_util_mac.h"
#import "url/gurl.h"

namespace {

typedef NS_ENUM(NSInteger, SectionIdentifier) {
  SectionIdentifierPrivacyContent = kSectionIdentifierEnumZero,
  SectionIdentifierSafeBrowsing,
  SectionIdentifierHTTPSOnlyMode,
  SectionIdentifierPasswordLeakCheck,
  SectionIdentifierWebServices,
  SectionIdentifierIncognitoAuth,
  SectionIdentifierIncognitoInterstitial,
  SectionIdentifierLockdownMode,
};

typedef NS_ENUM(NSInteger, ItemType) {
  ItemTypeClearBrowsingDataClear = kItemTypeEnumZero,
  // Footer to suggest the user to open Sync and Google services settings.
  ItemTypePrivacyFooter,
  ItemTypeOtherDevicesHandoff,
  ItemTypeIncognitoReauth,
  ItemTypeIncognitoReauthDisabled,
  ItemTypePrivacySafeBrowsing,
  ItemTypeIncognitoLock,
  ItemTypeIncognitoLockDisabled,
  ItemTypeHTTPSOnlyMode,
  ItemTypePasswordLeakCheck,
  ItemTypeIncognitoInterstitial,
  ItemTypeIncognitoInterstitialDisabled,
  ItemTypeLockdownMode,
};

// Used to open the Sync and Google Services settings.
// These links should not be dispatched.
const char kGoogleServicesSettingsURL[] = "settings://open_google_services";
const char kSyncSettingsURL[] = "settings://open_sync";

}  // namespace

@interface PrivacyTableViewController () <BooleanObserver,
                                          BrowserObserving,
                                          PopoverLabelViewControllerDelegate,
                                          PrefObserverDelegate,
                                          SyncObserverModelBridge> {
  raw_ptr<ProfileIOS> _profile;  // weak

  std::unique_ptr<BrowserObserverBridge> _browserObserverBridge;

  // Pref observer to track changes to prefs.
  std::unique_ptr<PrefObserverBridge> _prefObserverBridge;
  // Registrar for pref changes notifications.
  PrefChangeRegistrar _prefChangeRegistrar;
  // Sync Observer.
  std::unique_ptr<SyncObserverBridge> _syncObserver;

  // Updatable Items.
  TableViewDetailIconItem* _handoffDetailItem;
  // Safe Browsing item.
  TableViewDetailIconItem* _safeBrowsingDetailItem;
  // Incognito Lock item.
  TableViewDetailIconItem* _incognitoLockItem;
  // Lockdown Mode item.
  TableViewDetailIconItem* _lockdownModeDetailItem;

  // Whether Settings have been dismissed.
  BOOL _settingsAreDismissed;

  // Registrar for local pref changes notifications.
  PrefChangeRegistrar _localStateChangeRegistrar;
}

// Accessor for the incognito reauth pref.
@property(nonatomic, strong) PrefBackedBoolean* incognitoReauthPref;

// Accessor for the incognito soft lock pref.
@property(nonatomic, strong) PrefBackedBoolean* incognitoSoftLockPref;

// Switch item for toggling incognito reauth.
@property(nonatomic, strong) TableViewSwitchItem* incognitoReauthItem;

// Authentication module used when the user toggles the biometric auth on.
@property(nonatomic, strong) id<ReauthenticationProtocol> reauthModule;

// Accessor for the HTTPS-Only Mode pref.
@property(nonatomic, strong) PrefBackedBoolean* HTTPSOnlyModePref;

// The item related to the switch for the "HTTPS Only Mode" setting.
@property(nonatomic, strong) TableViewSwitchItem* HTTPSOnlyModeItem;

// Accessor for the Password leak check pref.
@property(nonatomic, strong) PrefBackedBoolean* passwordLeakCheckPref;

// The item related to the switch for the "Password leak check" setting.
@property(nonatomic, strong) TableViewSwitchItem* passwordLeakCheckItem;

// Accessor for the Incognito interstitial pref.
@property(nonatomic, strong) PrefBackedBoolean* incognitoInterstitialPref;

// The item related to the Incognito interstitial setting.
@property(nonatomic, strong) TableViewSwitchItem* incognitoInterstitialItem;

@end

@implementation PrivacyTableViewController

#pragma mark - Initialization

- (instancetype)initWithBrowser:(Browser*)browser
         reauthenticationModule:(id<ReauthenticationProtocol>)reauthModule {
  DCHECK(browser);

  self = [super initWithStyle:ChromeTableViewStyle()];
  if (self) {
    _reauthModule = reauthModule;
    _profile = browser->GetProfile();
    _browserObserverBridge =
        std::make_unique<BrowserObserverBridge>(browser, self);
    self.title = l10n_util::GetNSString(IDS_IOS_SETTINGS_PRIVACY_TITLE);

    PrefService* prefService = _profile->GetPrefs();

    _prefChangeRegistrar.Init(prefService);
    _localStateChangeRegistrar.Init(GetApplicationContext()->GetLocalState());

    _prefObserverBridge.reset(new PrefObserverBridge(self));
    // Register to observe any changes on pref-backed values displayed by the
    // screen.
    _prefObserverBridge->ObserveChangesForPreference(
        prefs::kIosHandoffToOtherDevices, &_prefChangeRegistrar);
    _prefObserverBridge->ObserveChangesForPreference(
        prefs::kSafeBrowsingEnabled, &_prefChangeRegistrar);
    _prefObserverBridge->ObserveChangesForPreference(
        prefs::kSafeBrowsingEnhanced, &_prefChangeRegistrar);
    _prefObserverBridge->ObserveChangesForPreference(
        prefs::kBrowserLockdownModeEnabled, &_localStateChangeRegistrar);
    _syncObserver.reset(new SyncObserverBridge(
        self, SyncServiceFactory::GetForProfile(_profile)));

    _incognitoReauthPref = [[PrefBackedBoolean alloc]
        initWithPrefService:GetApplicationContext()->GetLocalState()
                   prefName:prefs::kIncognitoAuthenticationSetting];
    [_incognitoReauthPref setObserver:self];

    if (IsIOSSoftLockEnabled()) {
      _incognitoSoftLockPref = [[PrefBackedBoolean alloc]
          initWithPrefService:GetApplicationContext()->GetLocalState()
                     prefName:prefs::kIncognitoSoftLockSetting];
      [_incognitoSoftLockPref setObserver:self];
    }
    _HTTPSOnlyModePref = [[PrefBackedBoolean alloc]
        initWithPrefService:prefService
                   prefName:prefs::kHttpsOnlyModeEnabled];
    [_HTTPSOnlyModePref setObserver:self];

    if (base::FeatureList::IsEnabled(
            safe_browsing::kMovePasswordLeakDetectionToggleIos)) {
      _passwordLeakCheckPref = [[PrefBackedBoolean alloc]
          initWithPrefService:prefService
                     prefName:password_manager::prefs::
                                  kPasswordLeakDetectionEnabled];
      [_passwordLeakCheckPref setObserver:self];
    }

    _incognitoInterstitialPref = [[PrefBackedBoolean alloc]
        initWithPrefService:GetApplicationContext()->GetLocalState()
                   prefName:prefs::kIncognitoInterstitialEnabled];
    [_incognitoInterstitialPref setObserver:self];
  }
  return self;
}

#pragma mark - UIViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  self.tableView.accessibilityIdentifier = kPrivacyTableViewId;

  [self loadModel];
}

- (void)didMoveToParentViewController:(UIViewController*)parent {
  [super didMoveToParentViewController:parent];
  if (!parent) {
    [self.presentationDelegate privacyTableViewControllerWasRemoved:self];
  }
}

#pragma mark - Public

- (void)disconnect {
  // Stop observable prefs.
  [_incognitoReauthPref stop];
  _incognitoReauthPref.observer = nil;
  _incognitoReauthPref = nil;

  if (IsIOSSoftLockEnabled()) {
    [_incognitoSoftLockPref stop];
    _incognitoSoftLockPref.observer = nil;
    _incognitoSoftLockPref = nil;
  }

  [_HTTPSOnlyModePref stop];
  _HTTPSOnlyModePref.observer = nil;
  _HTTPSOnlyModePref = nil;
  if (base::FeatureList::IsEnabled(
          safe_browsing::kMovePasswordLeakDetectionToggleIos)) {
    [_passwordLeakCheckPref stop];
    _passwordLeakCheckPref.observer = nil;
    _passwordLeakCheckPref = nil;
  }
  [_incognitoInterstitialPref stop];
  _incognitoInterstitialPref.observer = nil;
  _incognitoInterstitialPref = nil;

  // Remove pref changes registrations.
  _prefChangeRegistrar.RemoveAll();
  _localStateChangeRegistrar.Reset();

  // Remove observer bridges.
  _prefObserverBridge.reset();
  _browserObserverBridge.reset();

  // Remove sync observer.
  _syncObserver.reset();

  // Clear C++ ivars.
  _profile = nullptr;
}

#pragma mark - LegacyChromeTableViewController

- (void)loadModel {
  [super loadModel];
  if (_settingsAreDismissed) {
    return;
  }

  TableViewModel* model = self.tableViewModel;
  [model addSectionWithIdentifier:SectionIdentifierPrivacyContent];
  [model addSectionWithIdentifier:SectionIdentifierSafeBrowsing];

  [model addSectionWithIdentifier:SectionIdentifierHTTPSOnlyMode];
  [model addItem:self.HTTPSOnlyModeItem
      toSectionWithIdentifier:SectionIdentifierHTTPSOnlyMode];
  if (base::FeatureList::IsEnabled(
          safe_browsing::kMovePasswordLeakDetectionToggleIos)) {
    [model addSectionWithIdentifier:SectionIdentifierPasswordLeakCheck];
    [model addItem:self.passwordLeakCheckItem
        toSectionWithIdentifier:SectionIdentifierPasswordLeakCheck];
  }
  [model addSectionWithIdentifier:SectionIdentifierWebServices];
  [model addSectionWithIdentifier:SectionIdentifierIncognitoAuth];
  [model addSectionWithIdentifier:SectionIdentifierIncognitoInterstitial];
  [model addSectionWithIdentifier:SectionIdentifierLockdownMode];

  // Clear Browsing item.
  [model addItem:[self clearBrowsingDetailItem]
      toSectionWithIdentifier:SectionIdentifierPrivacyContent];

  // Privacy Safe Browsing item.
  [model addItem:[self safeBrowsingDetailItem]
      toSectionWithIdentifier:SectionIdentifierSafeBrowsing];

  // Web Services item.
  [model addItem:[self handoffDetailItem]
      toSectionWithIdentifier:SectionIdentifierWebServices];

  if (IsIOSSoftLockEnabled()) {
    // Incognito Lock item.
    TableViewItem* incognitoLockItem =
        IsIncognitoModeDisabled(_profile->GetPrefs())
            ? self.incognitoLockItemDisabled
            : self.incognitoLockItem;
    [model addItem:incognitoLockItem
        toSectionWithIdentifier:SectionIdentifierIncognitoAuth];
  } else {
    // Incognito reauth item is added. If Incognito mode is disabled, or device
    // authentication is not supported, a disabled version is shown instead with
    // relevant information as a popover.
    TableViewItem* incognitoReauthItem =
        (IsIncognitoModeDisabled(_profile->GetPrefs()) ||
         ![self deviceSupportsAuthentication])
            ? self.incognitoReauthItemDisabled
            : self.incognitoReauthItem;
    [model addItem:incognitoReauthItem
        toSectionWithIdentifier:SectionIdentifierIncognitoAuth];
  }

  // Show "Ask to Open Links from Other Apps in Incognito" setting.
  // Incognito interstitial item is added. If Incognito mode is
  // disabled or forced, a disabled version is shown with information
  // to learn more.
  TableViewItem* incognitoInterstitialItem =
      (IsIncognitoModeDisabled(_profile->GetPrefs()) ||
       IsIncognitoModeForced(_profile->GetPrefs()))
          ? self.incognitoInterstitialItemDisabled
          : self.incognitoInterstitialItem;
  [model addItem:incognitoInterstitialItem
      toSectionWithIdentifier:SectionIdentifierIncognitoInterstitial];

  // Lockdown Mode item.
  [model addItem:[self lockdownModeDetailItem]
      toSectionWithIdentifier:SectionIdentifierLockdownMode];
  [model setFooter:[self showPrivacyFooterItem]
      forSectionWithIdentifier:SectionIdentifierLockdownMode];
}

#pragma mark - Model Objects

- (TableViewSwitchItem*)HTTPSOnlyModeItem {
  if (!_HTTPSOnlyModeItem) {
    _HTTPSOnlyModeItem =
        [[TableViewSwitchItem alloc] initWithType:ItemTypeHTTPSOnlyMode];

    _HTTPSOnlyModeItem.text =
        l10n_util::GetNSString(IDS_IOS_SETTINGS_HTTPS_ONLY_MODE_TITLE);
    _HTTPSOnlyModeItem.detailText =
        l10n_util::GetNSString(IDS_IOS_SETTINGS_HTTPS_ONLY_MODE_DESCRIPTION);
    _HTTPSOnlyModeItem.on = [self.HTTPSOnlyModePref value];
    _HTTPSOnlyModeItem.target = self;
    _HTTPSOnlyModeItem.selector = @selector(HTTPSOnlyModeSwitchToggled:);
    _HTTPSOnlyModeItem.accessibilityIdentifier = kSettingsHttpsOnlyModeCellId;
  }
  return _HTTPSOnlyModeItem;
}

- (TableViewSwitchItem*)passwordLeakCheckItem {
  if (!_passwordLeakCheckItem) {
    _passwordLeakCheckItem =
        [[TableViewSwitchItem alloc] initWithType:ItemTypePasswordLeakCheck];

    _passwordLeakCheckItem.text = l10n_util::GetNSString(
        IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_LEAK_CHECK_TITLE);
    _passwordLeakCheckItem.detailText = l10n_util::GetNSString(
        IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_LEAK_CHECK_FRIENDLIER_SUMMARY);
    _passwordLeakCheckItem.on = [self.passwordLeakCheckPref value];
    _passwordLeakCheckItem.enabled = YES;
    _passwordLeakCheckItem.target = self;
    _passwordLeakCheckItem.selector =
        @selector(PasswordLeakCheckSwitchToggled:);
    _passwordLeakCheckItem.accessibilityIdentifier =
        kSafeBrowsingStandardProtectionPasswordLeakCellId;
  }
  return _passwordLeakCheckItem;
}

- (TableViewSwitchItem*)incognitoInterstitialItem {
  if (!_incognitoInterstitialItem) {
    _incognitoInterstitialItem = [[TableViewSwitchItem alloc]
        initWithType:ItemTypeIncognitoInterstitial];
    _incognitoInterstitialItem.text =
        l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_INCOGNITO_INTERSTITIAL);
    _incognitoInterstitialItem.on = self.incognitoInterstitialPref.value;
    _incognitoInterstitialItem.target = self;
    _incognitoInterstitialItem.selector =
        @selector(incognitoInterstitialSwitchToggled:);
    _incognitoInterstitialItem.enabled = YES;
    _incognitoInterstitialItem.accessibilityIdentifier =
        kSettingsIncognitoInterstitialId;
  }
  return _incognitoInterstitialItem;
}

- (TableViewInfoButtonItem*)incognitoInterstitialItemDisabled {
  TableViewInfoButtonItem* itemDisabled = [[TableViewInfoButtonItem alloc]
      initWithType:ItemTypeIncognitoInterstitialDisabled];
  itemDisabled.text =
      l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_INCOGNITO_INTERSTITIAL);
  itemDisabled.statusText = l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
  itemDisabled.accessibilityIdentifier =
      kSettingsIncognitoInterstitialDisabledId;
  itemDisabled.iconTintColor = [UIColor colorNamed:kGrey300Color];
  itemDisabled.textColor = [UIColor colorNamed:kTextSecondaryColor];
  itemDisabled.target = self;
  itemDisabled.selector =
      @selector(didTapIncognitoInterstitialDisabledInfoButton:);
  return itemDisabled;
}

- (TableViewItem*)handoffDetailItem {
  NSString* detailText =
      _profile->GetPrefs()->GetBoolean(prefs::kIosHandoffToOtherDevices)
          ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
          : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
  _handoffDetailItem = [self
           detailItemWithType:ItemTypeOtherDevicesHandoff
                      titleId:IDS_IOS_OPTIONS_ENABLE_HANDOFF_TO_OTHER_DEVICES
                   detailText:detailText
      accessibilityIdentifier:kSettingsHandoffCellId];

  return _handoffDetailItem;
}

// Creates TableViewHeaderFooterItem instance to show a link to open the Sync
// and Google services settings.
- (TableViewHeaderFooterItem*)showPrivacyFooterItem {
  TableViewLinkHeaderFooterItem* showPrivacyFooterItem =
      [[TableViewLinkHeaderFooterItem alloc]
          initWithType:ItemTypePrivacyFooter];

  NSString* privacyFooterText;

  syncer::SyncService* syncService =
      SyncServiceFactory::GetForProfile(_profile);

  NSMutableArray* urls = [[NSMutableArray alloc] init];
  if (!syncService->GetAccountInfo().IsEmpty()) {
    // Footer for signed in users.
    privacyFooterText = l10n_util::GetNSString(
        IDS_IOS_PRIVACY_ACCOUNT_SETTINGS_AND_GOOGLE_SERVICES_FOOTER);
    [urls addObject:[[CrURL alloc] initWithGURL:GURL(kSyncSettingsURL)]];
  } else {
    // Footer for signed out users.
    privacyFooterText =
        l10n_util::GetNSString(IDS_IOS_PRIVACY_SIGNED_OUT_FOOTER);
  }
  [urls
      addObject:[[CrURL alloc] initWithGURL:GURL(kGoogleServicesSettingsURL)]];

  showPrivacyFooterItem.text = privacyFooterText;
  showPrivacyFooterItem.urls = urls;
  return showPrivacyFooterItem;
}

- (void)updatePrivacyFooterItem {
  // The user might sign out from account settings, and thus the footer should
  // change.
  DCHECK([self.tableViewModel
      hasSectionForSectionIdentifier:SectionIdentifierLockdownMode]);
  [self.tableViewModel setFooter:[self showPrivacyFooterItem]
        forSectionWithIdentifier:SectionIdentifierLockdownMode];
  NSUInteger sectionIndex = [self.tableViewModel
      sectionForSectionIdentifier:SectionIdentifierLockdownMode];
  [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                withRowAnimation:UITableViewRowAnimationNone];
}

- (TableViewItem*)clearBrowsingDetailItem {
  return [self detailItemWithType:ItemTypeClearBrowsingDataClear
                          titleId:IDS_IOS_CLEAR_BROWSING_DATA_TITLE
                       detailText:nil
          accessibilityIdentifier:kSettingsClearBrowsingDataCellId];
}

- (TableViewItem*)safeBrowsingDetailItem {
  NSString* detailText = [self safeBrowsingDetailText];
  _safeBrowsingDetailItem =
      [self detailItemWithType:ItemTypePrivacySafeBrowsing
                          titleId:IDS_IOS_PRIVACY_SAFE_BROWSING_TITLE
                       detailText:detailText
          accessibilityIdentifier:kSettingsPrivacySafeBrowsingCellId];
  return _safeBrowsingDetailItem;
}

- (TableViewItem*)incognitoLockItem {
  _incognitoLockItem =
      [self detailItemWithType:ItemTypeIncognitoLock
                          titleId:IDS_IOS_INCOGNITO_LOCK_SETTING_NAME
                       detailText:[self incognitoLockDetailText]
          accessibilityIdentifier:kSettingsIncognitoLockCellId];
  return _incognitoLockItem;
}

- (TableViewInfoButtonItem*)incognitoLockItemDisabled {
  TableViewInfoButtonItem* itemDisabled = [[TableViewInfoButtonItem alloc]
      initWithType:ItemTypeIncognitoLockDisabled];
  itemDisabled.text =
      l10n_util::GetNSString(IDS_IOS_INCOGNITO_LOCK_SETTING_NAME);
  itemDisabled.statusText = l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
  itemDisabled.iconTintColor = [UIColor colorNamed:kGrey300Color];
  itemDisabled.textColor = [UIColor colorNamed:kTextSecondaryColor];
  itemDisabled.target = self;
  itemDisabled.selector = @selector(didTapIncognitoLockDisabledInfoButton:);
  return itemDisabled;
}

- (TableViewItem*)lockdownModeDetailItem {
  NSString* detailText = GetApplicationContext()->GetLocalState()->GetBoolean(
                             prefs::kBrowserLockdownModeEnabled)
                             ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
                             : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
  _lockdownModeDetailItem =
      [self detailItemWithType:ItemTypeLockdownMode
                          titleId:IDS_IOS_LOCKDOWN_MODE_TITLE
                       detailText:detailText
          accessibilityIdentifier:kPrivacyLockdownModeCellId];
  return _lockdownModeDetailItem;
}

- (TableViewSwitchItem*)incognitoReauthItem {
  if (_incognitoReauthItem) {
    return _incognitoReauthItem;
  }
  _incognitoReauthItem =
      [[TableViewSwitchItem alloc] initWithType:ItemTypeIncognitoReauth];
  _incognitoReauthItem.text =
      l10n_util::GetNSString(IDS_IOS_INCOGNITO_REAUTH_SETTING_NAME);
  _incognitoReauthItem.on = self.incognitoReauthPref.value;
  _incognitoReauthItem.enabled = YES;
  _incognitoReauthItem.target = self;
  _incognitoReauthItem.selector = @selector(incognitoReauthSwitchToggled:);
  return _incognitoReauthItem;
}

- (TableViewInfoButtonItem*)incognitoReauthItemDisabled {
  TableViewInfoButtonItem* itemDisabled = [[TableViewInfoButtonItem alloc]
      initWithType:ItemTypeIncognitoReauthDisabled];
  itemDisabled.text =
      l10n_util::GetNSString(IDS_IOS_INCOGNITO_REAUTH_SETTING_NAME);
  itemDisabled.statusText = l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
  itemDisabled.iconTintColor = [UIColor colorNamed:kGrey300Color];
  itemDisabled.textColor = [UIColor colorNamed:kTextSecondaryColor];
  itemDisabled.target = self;
  itemDisabled.selector = @selector(didTapIncognitoReauthDisabledInfoButton:);
  return itemDisabled;
}

- (TableViewDetailIconItem*)detailItemWithType:(NSInteger)type
                                       titleId:(NSInteger)titleId
                                    detailText:(NSString*)detailText
                       accessibilityIdentifier:
                           (NSString*)accessibilityIdentifier {
  TableViewDetailIconItem* detailItem =
      [[TableViewDetailIconItem alloc] initWithType:type];
  detailItem.text = l10n_util::GetNSString(titleId);
  detailItem.detailText = detailText;
  detailItem.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  detailItem.accessibilityTraits |= UIAccessibilityTraitButton;
  detailItem.accessibilityIdentifier = accessibilityIdentifier;

  return detailItem;
}

#pragma mark - SettingsControllerProtocol

- (void)reportDismissalUserAction {
  base::RecordAction(base::UserMetricsAction("MobilePrivacySettingsClose"));
}

- (void)reportBackUserAction {
  base::RecordAction(base::UserMetricsAction("MobilePrivacySettingsBack"));
}

- (void)settingsWillBeDismissed {
  DCHECK(!_settingsAreDismissed);

  [self disconnect];

  _settingsAreDismissed = YES;
}

#pragma mark - UITableViewDelegate

- (UIView*)tableView:(UITableView*)tableView
    viewForFooterInSection:(NSInteger)section {
  UIView* footerView = [super tableView:tableView
                 viewForFooterInSection:section];
  TableViewLinkHeaderFooterView* footer =
      base::apple::ObjCCast<TableViewLinkHeaderFooterView>(footerView);
  if (footer) {
    footer.delegate = self;
  }
  return footerView;
}

- (void)tableView:(UITableView*)tableView
    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
  [super tableView:tableView didSelectRowAtIndexPath:indexPath];
  NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath];
  switch (itemType) {
    case ItemTypeOtherDevicesHandoff:
      [self.handler showHandoff];
      break;
    case ItemTypeClearBrowsingDataClear:
      [self.handler showClearBrowsingData];
      break;
    case ItemTypePrivacySafeBrowsing:
      base::RecordAction(base::UserMetricsAction(
          "SafeBrowsing.Settings.ShowedFromParentSettings"));
      [self.handler showSafeBrowsing];
      break;
    case ItemTypeLockdownMode:
      [self.handler showLockdownMode];
      break;
    case ItemTypeIncognitoLock:
      [self.handler showIncognitoLock];
      break;
    default:
      break;
  }
  [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

#pragma mark - PrefObserverDelegate

- (void)onPreferenceChanged:(const std::string&)preferenceName {
  if (_settingsAreDismissed) {
    return;
  }

  [self enhancedSafeBrowsingInlinePromoTriggerCriteriaMet];

  if (preferenceName == prefs::kIosHandoffToOtherDevices) {
    NSString* detailText = _profile->GetPrefs()->GetBoolean(preferenceName)
                               ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
                               : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
    _handoffDetailItem.detailText = detailText;
    [self reconfigureCellsForItems:@[ _handoffDetailItem ]];
    return;
  }

  DCHECK(_safeBrowsingDetailItem);
  if (preferenceName == prefs::kSafeBrowsingEnabled ||
      preferenceName == prefs::kSafeBrowsingEnhanced) {
    _safeBrowsingDetailItem.detailText = [self safeBrowsingDetailText];
    [self reconfigureCellsForItems:@[ _safeBrowsingDetailItem ]];
  }

  if (preferenceName == prefs::kBrowserLockdownModeEnabled) {
    NSString* detailText =
        GetApplicationContext()->GetLocalState()->GetBoolean(preferenceName)
            ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
            : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
    _lockdownModeDetailItem.detailText = detailText;
    [self reconfigureCellsForItems:@[ _lockdownModeDetailItem ]];
    return;
  }
}

#pragma mark - BooleanObserver

- (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean {
  // Update the cells.
  if (IsIOSSoftLockEnabled()) {
    _incognitoLockItem.detailText = [self incognitoLockDetailText];
    [self reconfigureCellsForItems:@[ _incognitoLockItem ]];
  }
  self.incognitoReauthItem.on = self.incognitoReauthPref.value;
  [self reconfigureCellsForItems:@[ self.incognitoReauthItem ]];

  self.HTTPSOnlyModeItem.on = self.HTTPSOnlyModePref.value;
  [self reconfigureCellsForItems:@[ self.HTTPSOnlyModeItem ]];

  if (base::FeatureList::IsEnabled(
          safe_browsing::kMovePasswordLeakDetectionToggleIos)) {
    self.passwordLeakCheckItem.on = self.passwordLeakCheckPref.value;
    [self reconfigureCellsForItems:@[ self.passwordLeakCheckItem ]];
  }

  self.incognitoInterstitialItem.on = self.incognitoInterstitialPref.value;
  [self reconfigureCellsForItems:@[ self.incognitoInterstitialItem ]];
}

#pragma mark - TableViewLinkHeaderFooterItemDelegate

- (void)view:(TableViewLinkHeaderFooterView*)view didTapLinkURL:(CrURL*)URL {
  if (URL.gurl == GURL(kGoogleServicesSettingsURL)) {
    // kGoogleServicesSettingsURL is not a real link. It should be handled
    // with a special case.
    [self.settingsHandler showGoogleServicesSettingsFromViewController:self];
  } else if (URL.gurl == GURL(kSyncSettingsURL)) {
    [self.presentationDelegate showSyncSettingsWithViewController:self];
  } else {
    [super view:view didTapLinkURL:URL];
  }
}

#pragma mark - PopoverLabelViewControllerDelegate

- (void)didTapLinkURL:(NSURL*)URL {
  [super view:nil didTapLinkURL:[[CrURL alloc] initWithNSURL:URL]];
}

#pragma mark - SyncObserverModelBridge

- (void)onSyncStateChanged {
  [self updatePrivacyFooterItem];
}

#pragma mark - BrowserObserving

- (void)browserDestroyed:(Browser*)browser {
  [self disconnect];
}

#pragma mark - Private

- (void)didTapIncognitoLockDisabledInfoButton:(UIButton*)buttonView {
  InfoPopoverViewController* popover;
  if (supervised_user::IsSubjectToParentalControls(_profile)) {
    popover = [[SupervisedUserInfoPopoverViewController alloc]
        initWithMessage:
            l10n_util::GetNSString(
                IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED_BY_PARENT)];
  } else {
    popover = [[EnterpriseInfoPopoverViewController alloc]
        initWithMessage:l10n_util::GetNSString(
                            IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED)
         enterpriseName:nil];
  }
  [self showInfoPopover:popover forInfoButton:buttonView];
}

// Called when the user taps on the information button of the disabled Incognito
// reauth setting's UI cell.
- (void)didTapIncognitoReauthDisabledInfoButton:(UIButton*)buttonView {
  InfoPopoverViewController* popover;
  if (supervised_user::IsSubjectToParentalControls(_profile)) {
    popover = [[SupervisedUserInfoPopoverViewController alloc]
        initWithMessage:
            l10n_util::GetNSString(
                IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED_BY_PARENT)];
  } else if (IsIncognitoModeDisabled(_profile->GetPrefs())) {
    popover = [[EnterpriseInfoPopoverViewController alloc]
        initWithMessage:l10n_util::GetNSString(
                            IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED)
         enterpriseName:nil];
  } else {
    popover = [[InfoPopoverViewController alloc]
        initWithMessage:l10n_util::GetNSString(
                            IDS_IOS_INCOGNITO_REAUTH_SET_UP_PASSCODE_HINT)];
  }

  [self showInfoPopover:popover forInfoButton:buttonView];
}

// Called when the user taps on the information button of the disabled Incognito
// interstitial setting's UI cell.
- (void)didTapIncognitoInterstitialDisabledInfoButton:(UIButton*)buttonView {
  InfoPopoverViewController* popover;
  if (supervised_user::IsSubjectToParentalControls(_profile)) {
    popover = [[SupervisedUserInfoPopoverViewController alloc]
        initWithMessage:
            l10n_util::GetNSString(
                IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED_BY_PARENT)];
  } else {
    NSString* popoverMessage =
        IsIncognitoModeDisabled(_profile->GetPrefs())
            ? l10n_util::GetNSString(
                  IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED)
            : l10n_util::GetNSString(IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_FORCED);

    popover = [[EnterpriseInfoPopoverViewController alloc]
        initWithMessage:popoverMessage
         enterpriseName:nil];
  }

  [self showInfoPopover:popover forInfoButton:buttonView];
}

// Shows a contextual bubble explaining that the tapped setting is managed and
// includes a link to the chrome://management page.
- (void)showInfoPopover:(PopoverLabelViewController*)popover
          forInfoButton:(UIButton*)buttonView {
  popover.delegate = self;

  // Disable the button when showing the bubble.
  // The button will be enabled when closing the bubble in
  // (void)popoverPresentationControllerDidDismissPopover: of
  // EnterpriseInfoPopoverViewController.
  buttonView.enabled = NO;

  // Set the anchor and arrow direction of the bubble.
  popover.popoverPresentationController.sourceView = buttonView;
  popover.popoverPresentationController.sourceRect = buttonView.bounds;
  popover.popoverPresentationController.permittedArrowDirections =
      UIPopoverArrowDirectionAny;

  [self presentViewController:popover animated:YES completion:nil];
}

// Called from the HTTPS-Only Mode setting's UIControlEventValueChanged.
// When this is called, `switchView` already has the updated value:
// If the switch was off, and user taps it, when this method is called,
// switchView.on is YES.
- (void)HTTPSOnlyModeSwitchToggled:(UISwitch*)switchView {
  BOOL isOn = switchView.isOn;
  [_HTTPSOnlyModePref setValue:isOn];
  self.HTTPSOnlyModeItem.on = isOn;
  [self reconfigureCellsForItems:@[ self.HTTPSOnlyModeItem ]];
  [self enhancedSafeBrowsingInlinePromoTriggerCriteriaMet];
}

// Called from the Password Leak toggle setting's UIControlEventValueChanged.
// When this is called, `switchView` already has the updated value:
// If the switch was off, and user taps it, when this method is called,
// switchView.on is YES.
- (void)PasswordLeakCheckSwitchToggled:(UISwitch*)switchView {
  BOOL isOn = switchView.isOn;
  [_passwordLeakCheckPref setValue:isOn];
  self.passwordLeakCheckItem.on = isOn;
  [self reconfigureCellsForItems:@[ self.passwordLeakCheckItem ]];
}

// Called from the Incognito interstitial setting's UIControlEventValueChanged.
// When this is called, `switchView` already has the updated value:
// If the switch was off, and user taps it, when this method is called,
// switchView.on is YES.
- (void)incognitoInterstitialSwitchToggled:(UISwitch*)switchView {
  self.incognitoInterstitialPref.value = switchView.on;
  self.incognitoInterstitialItem.on = switchView.on;
  [self reconfigureCellsForItems:@[ self.incognitoInterstitialItem ]];
  UMA_HISTOGRAM_ENUMERATION(
      kIncognitoInterstitialSettingsActionsHistogram,
      switchView.on ? IncognitoInterstitialSettingsActions::kEnabled
                    : IncognitoInterstitialSettingsActions::kDisabled);
  [self enhancedSafeBrowsingInlinePromoTriggerCriteriaMet];
}

// Called from the reauthentication setting's UIControlEventValueChanged.
// When this is called, `switchView` already has the updated value:
// If the switch was off, and user taps it, when this method is called,
// switchView.on is YES.
- (void)incognitoReauthSwitchToggled:(UISwitch*)switchView {
  if (switchView.isOn && ![self.reauthModule canAttemptReauth]) {
    // This should normally not happen: the switch should not even be enabled.
    // Fallback behaviour added just in case.
    switchView.on = false;
    return;
  }

  __weak PrivacyTableViewController* weakSelf = self;
  [self.reauthModule
      attemptReauthWithLocalizedReason:
          l10n_util::GetNSString(
              IDS_IOS_INCOGNITO_REAUTH_SET_UP_SYSTEM_DIALOG_REASON)
                  canReusePreviousAuth:false
                               handler:^(ReauthenticationResult result) {
                                 BOOL enabled = switchView.on;
                                 if (result !=
                                     ReauthenticationResult::kSuccess) {
                                   // Revert the switch if authentication wasn't
                                   // successful.
                                   enabled = !enabled;
                                 }
                                 [switchView setOn:enabled animated:YES];
                                 weakSelf.incognitoReauthPref.value = enabled;
                                 weakSelf.incognitoReauthItem.on = enabled;
                                 [weakSelf reconfigureCellsForItems:@[
                                   weakSelf.incognitoReauthItem
                                 ]];
                                 [weakSelf
                                     enhancedSafeBrowsingInlinePromoTriggerCriteriaMet];
                               }];
}

// Checks if the device has Passcode, Face ID, or Touch ID set up.
- (BOOL)deviceSupportsAuthentication {
  LAContext* context = [[LAContext alloc] init];
  return [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication
                              error:nil];
}

// Returns the proper detail text for the incognito lock item depending on the
// incognito lock reauth and soft lock preference values.
- (NSString*)incognitoLockDetailText {
  if (_incognitoReauthPref.value) {
    return l10n_util::GetNSStringF(
        IDS_IOS_INCOGNITO_LOCK_SETTING_STATE_REAUTH,
        base::SysNSStringToUTF16(BiometricAuthenticationTypeString()));
  } else if (_incognitoSoftLockPref.value) {
    return l10n_util::GetNSString(IDS_IOS_SETTING_ON);
  }
  return l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
}

// Returns the proper detail text for the safe browsing item depending on the
// safe browsing and enhanced protection preference values.
- (NSString*)safeBrowsingDetailText {
  PrefService* prefService = _profile->GetPrefs();
  if (safe_browsing::IsEnhancedProtectionEnabled(*prefService)) {
    return l10n_util::GetNSString(
        IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_TITLE);
  } else if (safe_browsing::IsSafeBrowsingEnabled(*prefService)) {
    return l10n_util::GetNSString(
        IDS_IOS_PRIVACY_SAFE_BROWSING_STANDARD_PROTECTION_TITLE);
  }
  return l10n_util::GetNSString(
      IDS_IOS_PRIVACY_SAFE_BROWSING_NO_PROTECTION_DETAIL_TITLE);
}

- (void)enhancedSafeBrowsingInlinePromoTriggerCriteriaMet {
  if (!base::FeatureList::IsEnabled(
          feature_engagement::kIPHiOSInlineEnhancedSafeBrowsingPromoFeature) ||
      !_profile) {
    return;
  }
  feature_engagement::Tracker* tracker =
      feature_engagement::TrackerFactory::GetForProfile(_profile);
  // TODO(crbug.com/427478234): This event should be fired by the mediator.
  tracker->NotifyEvent(
      feature_engagement::events::kEnhancedSafeBrowsingPromoCriterionMet);
}

@end
