// Copyright 2022 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_safe_browsing_mediator.h"

#import "base/apple/foundation_util.h"
#import "base/auto_reset.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "base/notreached.h"
#import "build/branding_buildflags.h"
#import "components/prefs/pref_service.h"
#import "components/safe_browsing/core/common/features.h"
#import "components/safe_browsing/core/common/hashprefix_realtime/hash_realtime_utils.h"
#import "components/safe_browsing/core/common/safe_browsing_prefs.h"
#import "ios/chrome/browser/first_run/public/best_features_item.h"
#import "ios/chrome/browser/policy/model/policy_util.h"
#import "ios/chrome/browser/settings/model/sync/utils/sync_util.h"
#import "ios/chrome/browser/settings/ui_bundled/cells/settings_image_detail_text_item.h"
#import "ios/chrome/browser/settings/ui_bundled/privacy/privacy_constants.h"
#import "ios/chrome/browser/settings/ui_bundled/privacy/privacy_safe_browsing_consumer.h"
#import "ios/chrome/browser/settings/ui_bundled/privacy/privacy_safe_browsing_navigation_commands.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/utils/observable_boolean.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/symbols/symbols.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/util/uikit_ui_util.h"
#import "ios/chrome/browser/welcome_back/model/features.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"

using ItemArray = NSArray<TableViewItem*>*;

namespace {

// The size of the symbol image.
constexpr CGFloat kSymbolImagePointSize = 17.;

// List of item types.
typedef NS_ENUM(NSInteger, ItemType) {
  ItemTypeSafeBrowsingStandardProtection = kItemTypeEnumZero,
  ItemTypeSafeBrowsingEnhancedProtection,
  ItemTypeSafeBrowsingNoProtection,
};

}  // namespace

@interface PrivacySafeBrowsingMediator () <BooleanObserver>

// Preference value for the enhanced safe browsing feature.
@property(nonatomic, strong, readonly)
    PrefBackedBoolean* safeBrowsingEnhancedProtectionPreference;

// Preference value for the standard safe browsing feature.
@property(nonatomic, strong, readonly)
    PrefBackedBoolean* safeBrowsingStandardProtectionPreference;

// All the items for the safe browsing section.
@property(nonatomic, strong, readonly) ItemArray safeBrowsingItems;

// User pref service used to check if a specific pref is managed by enterprise
// policies.
@property(nonatomic, assign, readonly) PrefService* userPrefService;

// Boolean to check if safe browsing is controlled by enterprise.
@property(nonatomic, readonly) BOOL enterpriseEnabled;

@end

@implementation PrivacySafeBrowsingMediator

@synthesize safeBrowsingItems = _safeBrowsingItems;

- (instancetype)initWithUserPrefService:(PrefService*)userPrefService {
  self = [super init];
  if (self) {
    DCHECK(userPrefService);
    _userPrefService = userPrefService;
    _safeBrowsingEnhancedProtectionPreference = [[PrefBackedBoolean alloc]
        initWithPrefService:userPrefService
                   prefName:prefs::kSafeBrowsingEnhanced];
    _safeBrowsingEnhancedProtectionPreference.observer = self;
    _safeBrowsingStandardProtectionPreference = [[PrefBackedBoolean alloc]
        initWithPrefService:userPrefService
                   prefName:prefs::kSafeBrowsingEnabled];
    _safeBrowsingStandardProtectionPreference.observer = self;
  }
  return self;
}

- (void)disconnect {
  [_safeBrowsingEnhancedProtectionPreference stop];
  _safeBrowsingEnhancedProtectionPreference = nil;

  [_safeBrowsingStandardProtectionPreference stop];
  _safeBrowsingStandardProtectionPreference = nil;
}

- (void)selectSettingItem:(TableViewItem*)item {
  // If item is already selected or if we cancel turning off Safe Browsing,
  // don't do anything and keep the current selected choice.
  ItemType type = static_cast<ItemType>(item.type);
  if (item == nil || [self shouldItemTypeHaveCheckmark:type]) {
    [self updatePrivacySafeBrowsingSectionAndNotifyConsumer:YES];
    return;
  }

  // Show checkmark for selected item and update associated preference value by
  // setting the SafeBrowsingState.
  safe_browsing::SafeBrowsingState safeBrowsingState =
      safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING;
  switch (type) {
    case ItemTypeSafeBrowsingEnhancedProtection:
      safeBrowsingState = safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION;
      // Notify Welcome Back to remove Enhanced Safe Browsing from the eligible
      // features.
      if (IsWelcomeBackEnabled()) {
        MarkWelcomeBackFeatureUsed(BestFeaturesItemType::kEnhancedSafeBrowsing);
      }
      break;
    case ItemTypeSafeBrowsingStandardProtection:
      safeBrowsingState = safe_browsing::SafeBrowsingState::STANDARD_PROTECTION;
      break;
    case ItemTypeSafeBrowsingNoProtection:
      safeBrowsingState = safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING;
      break;
    default:
      NOTREACHED();
  }
  safe_browsing::SetSafeBrowsingState(self.userPrefService, safeBrowsingState);

  [self updatePrivacySafeBrowsingSectionAndNotifyConsumer:YES];
}

#pragma mark - Properties

- (ItemArray)safeBrowsingItems {
  if (!_safeBrowsingItems) {
    NSMutableArray* items = [NSMutableArray array];
    NSInteger enhancedProtectionSummary;
    enhancedProtectionSummary =
        IDS_IOS_PRIVACY_SAFE_BROWSING_ENHANCED_PROTECTION_SUMMARY_UPDATED;

    NSInteger standardProtectionSummary;
    standardProtectionSummary =
        IDS_IOS_PRIVACY_SAFE_BROWSING_STANDARD_PROTECTION_FRIENDLIER_SUMMARY;
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
    if (safe_browsing::hash_realtime_utils::
            IsHashRealTimeLookupEligibleInSession()) {
      standardProtectionSummary =
          IDS_IOS_PRIVACY_SAFE_BROWSING_STANDARD_PROTECTION_FRIENDLIER_SUMMARY_PROXY;
    }
#endif
    TableViewItem* safeBrowsingEnhancedProtectionItem = [self
             infoButtonItemType:ItemTypeSafeBrowsingEnhancedProtection
                        titleId:
                            IDS_IOS_PRIVACY_SAFE_BROWSING_ENHANCED_PROTECTION_TITLE
                     detailText:enhancedProtectionSummary
        accessibilityIdentifier:kSettingsSafeBrowsingEnhancedProtectionCellId];
    [items addObject:safeBrowsingEnhancedProtectionItem];

    if (base::FeatureList::IsEnabled(
            safe_browsing::kMovePasswordLeakDetectionToggleIos) &&
        base::FeatureList::IsEnabled(
            safe_browsing::kExtendedReportingRemovePrefDependency)) {
      TableViewItem* safeBrowsingStandardProtectionItem = [self
               detailIconItemType:ItemTypeSafeBrowsingStandardProtection
                          titleId:
                              IDS_IOS_PRIVACY_SAFE_BROWSING_STANDARD_PROTECTION_TITLE
                       detailText:standardProtectionSummary
          accessibilityIdentifier:
              kSettingsSafeBrowsingStandardProtectionCellId];
      [items addObject:safeBrowsingStandardProtectionItem];
    } else {
      TableViewItem* safeBrowsingStandardProtectionItem = [self
               infoButtonItemType:ItemTypeSafeBrowsingStandardProtection
                          titleId:
                              IDS_IOS_PRIVACY_SAFE_BROWSING_STANDARD_PROTECTION_TITLE
                       detailText:standardProtectionSummary
          accessibilityIdentifier:
              kSettingsSafeBrowsingStandardProtectionCellId];
      [items addObject:safeBrowsingStandardProtectionItem];
    }

    if (self.enterpriseEnabled) {
      TableViewItem* safeBrowsingNoProtectionItem = [self
               infoButtonItemType:ItemTypeSafeBrowsingNoProtection
                          titleId:
                              IDS_IOS_PRIVACY_SAFE_BROWSING_NO_PROTECTION_TITLE
                       detailText:
                           IDS_IOS_PRIVACY_SAFE_BROWSING_NO_PROTECTION_FRIENDLIER_SUMMARY
          accessibilityIdentifier:kSettingsSafeBrowsingNoProtectionCellId];
      [items addObject:safeBrowsingNoProtectionItem];
    } else {
      TableViewItem* safeBrowsingNoProtectionItem = [self
               detailIconItemType:ItemTypeSafeBrowsingNoProtection
                          titleId:
                              IDS_IOS_PRIVACY_SAFE_BROWSING_NO_PROTECTION_TITLE
                       detailText:
                           IDS_IOS_PRIVACY_SAFE_BROWSING_NO_PROTECTION_FRIENDLIER_SUMMARY
          accessibilityIdentifier:kSettingsSafeBrowsingNoProtectionCellId];
      [items addObject:safeBrowsingNoProtectionItem];
    }
    _safeBrowsingItems = items;
  }
  return _safeBrowsingItems;
}

- (void)setConsumer:(id<PrivacySafeBrowsingConsumer>)consumer {
  if (_consumer == consumer) {
    return;
  }
  _consumer = consumer;
  [_consumer setSafeBrowsingItems:self.safeBrowsingItems];
  [_consumer setEnterpriseEnabled:self.enterpriseEnabled];
}

- (BOOL)enterpriseEnabled {
  return self.userPrefService->IsManagedPreference(prefs::kSafeBrowsingEnabled);
}

#pragma mark - Private

// Creates item with an image checkmark and an info button.
- (TableViewItem*)infoButtonItemType:(NSInteger)type
                             titleId:(NSInteger)titleId
                          detailText:(NSInteger)detailText
             accessibilityIdentifier:(NSString*)accessibilityIdentifier {
  TableViewInfoButtonItem* infoButtonItem =
      [[TableViewInfoButtonItem alloc] initWithType:type];
  infoButtonItem.text = l10n_util::GetNSString(titleId);
  infoButtonItem.detailText = l10n_util::GetNSString(detailText);
  // If Safe Browsing is controlled by enterprise, make non-selected options
  // greyed out.
  if (self.enterpriseEnabled && ![self shouldItemTypeHaveCheckmark:type]) {
    // This item is not controllable; set to lighter colors.
    infoButtonItem.textColor = [UIColor colorNamed:kTextSecondaryColor];
    infoButtonItem.detailTextColor = [UIColor colorNamed:kTextTertiaryColor];
    infoButtonItem.accessibilityHint = l10n_util::GetNSString(
        IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
  } else {
    infoButtonItem.accessibilityActivationPointOnButton = NO;
    infoButtonItem.accessibilityHint = l10n_util::GetNSString(
        IDS_IOS_TABLE_VIEW_INFO_BUTTON_ITEM_ACCESSIBILITY_TAP);
  }
  UIImageConfiguration* configuration = [UIImageSymbolConfiguration
      configurationWithPointSize:kSymbolImagePointSize
                          weight:UIImageSymbolWeightSemibold
                           scale:UIImageSymbolScaleMedium];
  infoButtonItem.iconImage =
      SymbolWithConfiguration(SymbolCheckmark, configuration);
  infoButtonItem.iconTintColor = [self iconTintColorForItemType:type];
  infoButtonItem.accessibilityIdentifier = accessibilityIdentifier;
  infoButtonItem.target = self;
  infoButtonItem.selector = @selector(didTapInfoButton:);
  infoButtonItem.tag = type;

  return infoButtonItem;
}

// Creates item with an image checkmark.
- (TableViewItem*)detailIconItemType:(NSInteger)type
                             titleId:(NSInteger)titleId
                          detailText:(NSInteger)detailText
             accessibilityIdentifier:(NSString*)accessibilityIdentifier {
  TableViewDetailIconItem* detailIconItem =
      [[TableViewDetailIconItem alloc] initWithType:type];
  detailIconItem.text = l10n_util::GetNSString(titleId);
  detailIconItem.textNumberOfLines = 0;
  detailIconItem.detailText = l10n_util::GetNSString(detailText);
  detailIconItem.detailTextNumberOfLines = 0;
  detailIconItem.textLayoutConstraintAxis = UILayoutConstraintAxisVertical;

  UIImageConfiguration* configuration = [UIImageSymbolConfiguration
      configurationWithPointSize:kSymbolImagePointSize
                          weight:UIImageSymbolWeightSemibold
                           scale:UIImageSymbolScaleMedium];
  detailIconItem.iconImage =
      SymbolWithConfiguration(SymbolCheckmark, configuration);
  detailIconItem.iconTintColor = [self iconTintColorForItemType:type];
  detailIconItem.accessibilityIdentifier = accessibilityIdentifier;
  detailIconItem.accessibilityTraits = UIAccessibilityTraitButton;

  return detailIconItem;
}

// Returns whether an ItemType should have a checkmark based on its
// SafeBrowsingState.
- (BOOL)shouldItemTypeHaveCheckmark:(NSInteger)itemType {
  ItemType type = static_cast<ItemType>(itemType);
  safe_browsing::SafeBrowsingState safeBrowsingState =
      safe_browsing::GetSafeBrowsingState(*self.userPrefService);
  switch (type) {
    case ItemTypeSafeBrowsingEnhancedProtection:
      return safeBrowsingState ==
             safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION;
    case ItemTypeSafeBrowsingStandardProtection:
      return safeBrowsingState ==
             safe_browsing::SafeBrowsingState::STANDARD_PROTECTION;
    case ItemTypeSafeBrowsingNoProtection:
      return safeBrowsingState ==
             safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING;
    default:
      NOTREACHED();
  }
}

// Returns the color to use to tint the checkmark associated with `itemType`.
- (UIColor*)iconTintColorForItemType:(NSInteger)itemType {
  return [self shouldItemTypeHaveCheckmark:itemType]
             ? [UIColor colorNamed:kBlueColor]
             : [UIColor clearColor];
}

// Updates the privacy safe browsing section according to the user consent. If
// `notifyConsumer` is YES, the consumer is notified about model changes.
- (void)updatePrivacySafeBrowsingSectionAndNotifyConsumer:(BOOL)notifyConsumer {
  for (TableViewItem* item in self.safeBrowsingItems) {
    ItemType type = static_cast<ItemType>(item.type);
    if ([item isKindOfClass:TableViewInfoButtonItem.class]) {
      TableViewInfoButtonItem* infoButtonItem =
          base::apple::ObjCCast<TableViewInfoButtonItem>(item);
      infoButtonItem.iconTintColor = [self iconTintColorForItemType:type];
    } else if ([item isKindOfClass:TableViewDetailIconItem.class]) {
      TableViewDetailIconItem* infoDetailItem =
          base::apple::ObjCCast<TableViewDetailIconItem>(item);
      infoDetailItem.iconTintColor = [self iconTintColorForItemType:type];
    }
  }

  if (notifyConsumer) {
    [self.consumer reconfigureCellsForItems];
    [self selectItem];
  }
}

// Check if selected row should display enterprise popover.
- (BOOL)shouldEnterprisePopOverDisplay:(ItemType)itemType {
  return self.enterpriseEnabled &&
         (![self shouldItemTypeHaveCheckmark:itemType] ||
          itemType == ItemTypeSafeBrowsingNoProtection);
}

// Called when the user tap on an info button.
- (void)didTapInfoButton:(UIButton*)button {
  ItemType itemType = static_cast<ItemType>(button.tag);

  if ([self shouldEnterprisePopOverDisplay:itemType]) {
    [self.consumer showEnterprisePopUp:button];
    return;
  }

  switch (itemType) {
    case ItemTypeSafeBrowsingEnhancedProtection:
      base::RecordAction(base::UserMetricsAction(
          "SafeBrowsing.Settings.EnhancedProtectionExpandArrowClicked"));
      [self.handler showSafeBrowsingEnhancedProtection];
      break;
    case ItemTypeSafeBrowsingStandardProtection:
      base::RecordAction(base::UserMetricsAction(
          "SafeBrowsing.Settings.StandardProtectionExpandArrowClicked"));
      [self.handler showSafeBrowsingStandardProtection];
      break;
    default:
      NOTREACHED();
  }
}

#pragma mark - SafeBrowsingViewControllerDelegate

- (void)didSelectItem:(TableViewItem*)item {
  ItemType type = static_cast<ItemType>(item.type);
  if (type == ItemTypeSafeBrowsingEnhancedProtection) {
    if ([self shouldItemTypeHaveCheckmark:type]) {
      base::RecordAction(base::UserMetricsAction(
          "SafeBrowsing.Settings.EnhancedProtectionExpandArrowClicked"));
      [self.handler showSafeBrowsingEnhancedProtection];
    } else {
      base::RecordAction(base::UserMetricsAction(
          "SafeBrowsing.Settings.EnhancedProtectionClicked"));
      if (self.openedFromPromoInteraction) {
        base::RecordAction(base::UserMetricsAction(
            "SafeBrowsing.Settings.EnhancedProtectionClickedDueToPromo"));
      }
      [self selectSettingItem:item];
    }
  }

  if (type == ItemTypeSafeBrowsingStandardProtection) {
    if ([self shouldItemTypeHaveCheckmark:type]) {
      base::RecordAction(base::UserMetricsAction(
          "SafeBrowsing.Settings.StandardProtectionExpandArrowClicked"));
      [self.handler showSafeBrowsingStandardProtection];
    } else {
      base::RecordAction(base::UserMetricsAction(
          "SafeBrowsing.Settings.StandardProtectionClicked"));
      [self selectSettingItem:item];
    }
  }

  if (type == ItemTypeSafeBrowsingNoProtection &&
      ![self shouldItemTypeHaveCheckmark:type]) {
    base::RecordAction(base::UserMetricsAction(
        "SafeBrowsing.Settings.DisableSafeBrowsingClicked"));
    [self.handler showSafeBrowsingNoProtectionPopUp:item];
  }
}

- (void)selectItem {
  for (TableViewItem* item in self.safeBrowsingItems) {
    ItemType type = static_cast<ItemType>(item.type);
    if ([self shouldItemTypeHaveCheckmark:type]) {
      [self.consumer selectItem:item];
      break;
    }
  }
}

#pragma mark - BooleanObserver

- (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean {
  [self updatePrivacySafeBrowsingSectionAndNotifyConsumer:YES];
}

@end
