// 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/search_engine_table_view_controller.h"

#import <algorithm>
#import <functional>
#import <memory>

#import "base/apple/foundation_util.h"
#import "base/feature_list.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/password_manager/core/common/password_manager_features.h"
#import "components/prefs/pref_service.h"
#import "components/regional_capabilities/regional_capabilities_service.h"
#import "components/search_engines/search_engines_pref_names.h"
#import "components/search_engines/search_engines_switches.h"
#import "components/search_engines/template_url_service.h"
#import "components/search_engines/template_url_service_observer.h"
#import "components/search_engines/template_url_starter_pack_data.h"
#import "components/signin/public/base/signin_switches.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
#import "ios/chrome/browser/regional_capabilities/model/regional_capabilities_service_factory.h"
#import "ios/chrome/browser/search_engine_choice/ui/search_engine_choice_ui_util.h"
#import "ios/chrome/browser/search_engines/model/search_engine_observer_bridge.h"
#import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
#import "ios/chrome/browser/settings/ui_bundled/cells/settings_search_engine_item.h"
#import "ios/chrome/browser/settings/ui_bundled/settings_table_view_controller_constants.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/public/features/features.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/cells/table_view_url_item.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/grit/ios_strings.h"
#import "net/base/apple/url_conversions.h"
#import "ui/base/l10n/l10n_util_mac.h"

namespace {

typedef NS_ENUM(NSInteger, SectionIdentifier) {
  SectionIdentifierFirstList = kSectionIdentifierEnumZero,
  SectionIdentifierSecondList,
};

typedef NS_ENUM(NSInteger, ItemType) {
  ItemTypeHeader = kItemTypeEnumZero,
  // Used for prepopulated search engine.
  ItemTypePrepopulatedEngine,
  // Used for custom search engine.
  ItemTypeCustomEngine,
};

const CGFloat kTableViewSeparatorLeadingInset = 56;
constexpr base::TimeDelta kMaxVisitAge = base::Days(2);
const size_t kMaxcustomSearchEngines = 3;
const char kUmaSelectDefaultSearchEngine[] =
    "Search.iOS.SelectDefaultSearchEngine";

}  // namespace

@interface SearchEngineTableViewController () <SearchEngineObserving> {
  // Whether Settings have been dismissed.
  BOOL _settingsAreDismissed;
}

// Prevent unnecessary notifications when we write to the setting.
@property(nonatomic, assign) BOOL updatingBackend;

// Whether the search engines have changed while the backend was being updated.
@property(nonatomic, assign) BOOL searchEngineChangedInBackground;

@end

@implementation SearchEngineTableViewController {
  raw_ptr<TemplateURLService> _templateURLService;  // weak
  raw_ptr<PrefService> _prefService;                // weak
  raw_ptr<regional_capabilities::RegionalCapabilitiesService>
      _regionalCapabilitiesService;  // weak
  std::unique_ptr<SearchEngineObserverBridge> _observer;
  // The list of choice screen search engines retrieved from the
  // TemplateURLService.
  std::vector<std::unique_ptr<TemplateURL>> _choiceScreenTemplateURLs;
  // The first list in the page which contains prepopulated search engines and
  // search engines that are created by policy, and possibly one custom search
  // engine if it's selected as default search engine.
  // Note that `TemplateURL` pointers should not be freed. They either come from
  // `TemplateURLService::GetTemplateURLs()`,
  // `TemplateURLService::GetPrepopulatedAndRecentlyVisitedTemplateURLs()`, or
  // they are owned by `_choiceScreenTemplateUrls`.
  std::vector<raw_ptr<TemplateURL, DanglingUntriaged>> _firstList;
  // The second list in the page which contains all remaining custom search
  // engines.
  // Note that `TemplateURL` pointers should not be freed. They either come from
  // `TemplateURLService::GetTemplateURLs()`,
  // `TemplateURLService::GetPrepopulatedAndRecentlyVisitedTemplateURLs()`, or
  // they are owned by `_choiceScreenTemplateUrls`.
  std::vector<raw_ptr<TemplateURL, DanglingUntriaged>> _secondList;
  // FaviconLoader is a keyed service that uses LargeIconService to retrieve
  // favicon images.
  raw_ptr<FaviconLoader> _faviconLoader;
}

#pragma mark - Initialization

- (instancetype)initWithProfile:(ProfileIOS*)profile {
  DCHECK(profile);

  self = [super initWithStyle:ChromeTableViewStyle()];
  if (self) {
    _templateURLService =
        ios::TemplateURLServiceFactory::GetForProfile(profile);
    _observer =
        std::make_unique<SearchEngineObserverBridge>(self, _templateURLService);
    _templateURLService->Load();
    _faviconLoader = IOSChromeFaviconLoaderFactory::GetForProfile(profile);
    _prefService = profile->GetPrefs();

    _regionalCapabilitiesService =
        ios::RegionalCapabilitiesServiceFactory::GetForProfile(profile);

    [self setTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_ENGINE_SETTING_TITLE)];
    self.shouldDisableDoneButtonOnEdit = YES;
  }
  return self;
}

#pragma mark - Properties

- (void)setUpdatingBackend:(BOOL)updatingBackend {
  if (_updatingBackend == updatingBackend) {
    return;
  }

  _updatingBackend = updatingBackend;

  if (_updatingBackend) {
    // When `YES`, the view is going to update the backend. The model should
    // not be reloaded.
    return;
  }
  if (!self.searchEngineChangedInBackground) {
    // If `-[<SearchEngineObserving> searchEngineChanged]` was not called while
    // `_updatingBackend` was `YES`, there is no point to reload the search
    // engines list.
    return;
  }

  [self loadSearchEngines];
  self.searchEngineChangedInBackground = NO;

  BOOL hasSecondSection = [self.tableViewModel
      hasSectionForSectionIdentifier:SectionIdentifierSecondList];
  BOOL secondSectionExistenceChanged = hasSecondSection == _secondList.empty();
  BOOL numberOfCustomItemDifferent =
      secondSectionExistenceChanged ||
      (hasSecondSection &&
       [self.tableViewModel
           itemsInSectionWithIdentifier:SectionIdentifierSecondList]
               .count != _secondList.size());

  BOOL numberOfPrepopulatedItemDifferent =
      [self.tableViewModel
          itemsInSectionWithIdentifier:SectionIdentifierFirstList]
          .count != _firstList.size();

  if (numberOfPrepopulatedItemDifferent || numberOfCustomItemDifferent) {
    // The number of items has changed.
    [self reloadData];
    return;
  }

  NSArray* firstListItem = [self.tableViewModel
      itemsInSectionWithIdentifier:SectionIdentifierFirstList];
  for (NSUInteger index = 0; index < firstListItem.count; index++) {
    if (![self isItem:firstListItem[index]
            equalForTemplateURL:_firstList[index]]) {
      // Item has changed, reload the TableView.
      [self reloadData];
      return;
    }
  }

  if (hasSecondSection) {
    NSArray* secondListItem = [self.tableViewModel
        itemsInSectionWithIdentifier:SectionIdentifierSecondList];
    for (NSUInteger index = 0; index < secondListItem.count; index++) {
      if (![self isItem:secondListItem[index]
              equalForTemplateURL:_secondList[index]]) {
        // Item has changed, reload the TableView.
        [self reloadData];
        return;
      }
    }
  }
}

#pragma mark - UIViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  // With no header on first appearance, UITableView adds a 35 points space at
  // the beginning of the table view. This space remains after this table view
  // reloads with headers. Setting a small tableHeaderView avoids this.
  self.tableView.tableHeaderView =
      [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGFLOAT_MIN)];

  self.tableView.allowsMultipleSelectionDuringEditing = YES;
  self.tableView.separatorInset =
      UIEdgeInsetsMake(0, kTableViewSeparatorLeadingInset, 0, 0);

  [self updateUIForEditState];
  [self loadModel];
}

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  self.navigationController.toolbarHidden = NO;
}

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
  if (editing) {
    base::RecordAction(
        base::UserMetricsAction("IOS.SearchEngines.RecentlyViewed.Edit"));
  }

  [super setEditing:editing animated:animated];

  // Disable prepopulated engines and remove the checkmark in editing mode, and
  // recover them in normal mode.
  [self updatePrepopulatedEnginesForEditing:editing];
  [self updateUIForEditState];
}

#pragma mark - LegacyChromeTableViewController

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

  self.tableView.accessibilityIdentifier = kSearchEngineTableViewControllerId;
  TableViewModel* model = self.tableViewModel;
  [self loadSearchEngines];

  // Add prior search engines.
  if (_firstList.size() > 0) {
    [model addSectionWithIdentifier:SectionIdentifierFirstList];

    if (_regionalCapabilitiesService->IsInSearchEngineChoiceScreenRegion()) {
      TableViewTextHeaderFooterItem* header =
          [[TableViewTextHeaderFooterItem alloc] initWithType:ItemTypeHeader];
      header.subtitle =
          l10n_util::GetNSString(IDS_SEARCH_ENGINE_CHOICE_SETTINGS_SUBTITLE);
      [model setHeader:header
          forSectionWithIdentifier:SectionIdentifierFirstList];
    }

    for (const TemplateURL* templateURL : _firstList) {
      TableViewItem* item = nil;
      item = [self createSettingsSearchEngineItemFromTemplateURL:templateURL];
      [model addItem:item toSectionWithIdentifier:SectionIdentifierFirstList];
    }
  }

  // Add custom search engines.
  if (_secondList.size() > 0) {
    [model addSectionWithIdentifier:SectionIdentifierSecondList];

    TableViewTextHeaderFooterItem* header =
        [[TableViewTextHeaderFooterItem alloc] initWithType:ItemTypeHeader];
    header.text = l10n_util::GetNSString(
        IDS_IOS_SEARCH_ENGINE_SETTING_CUSTOM_SECTION_HEADER);
    [model setHeader:header
        forSectionWithIdentifier:SectionIdentifierSecondList];

    for (const TemplateURL* templateURL : _secondList) {
      DCHECK(templateURL->prepopulate_id() == 0);
      TableViewItem* item = nil;
      item = [self createSettingsSearchEngineItemFromTemplateURL:templateURL];
      [model addItem:item toSectionWithIdentifier:SectionIdentifierSecondList];
    }
  }
  // The toolbar edit button's state needs to be updated.
  [self updatedToolbarForEditState];
}

#pragma mark - SettingsControllerProtocol

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

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

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

  // Remove observer bridges.
  _observer.reset();

  // Clear C++ ivars.
  _templateURLService = nullptr;
  _prefService = nullptr;
  _regionalCapabilitiesService = nullptr;
  _faviconLoader = nullptr;

  _settingsAreDismissed = YES;
}

#pragma mark - SettingsRootTableViewController

- (void)deleteItems:(NSArray<NSIndexPath*>*)indexPaths {
  base::RecordAction(
      base::UserMetricsAction("IOS.SearchEngines.RecentlyViewed.Delete"));
  // Do not call super as this also deletes the section if it is empty.
  [self deleteItemAtIndexPaths:indexPaths];
}

- (BOOL)shouldHideToolbar {
  return NO;
}

- (BOOL)shouldShowEditDoneButton {
  return NO;
}

- (BOOL)editButtonEnabled {
  // The custom search engine can only be deleted when they are not selected
  // (so in the second section).
  return [self.tableViewModel hasItemForItemType:ItemTypeCustomEngine
                               sectionIdentifier:SectionIdentifierSecondList];
}

- (void)updateUIForEditState {
  [super updateUIForEditState];
  [self updatedToolbarForEditState];
}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView*)tableView
    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
  [super tableView:tableView didSelectRowAtIndexPath:indexPath];
  if (_settingsAreDismissed) {
    return;
  }

  // Keep selection in editing mode.
  if (self.editing) {
    self.deleteButton.enabled = YES;
    return;
  }

  [tableView deselectRowAtIndexPath:indexPath animated:YES];
  TableViewModel* model = self.tableViewModel;
  TableViewItem* selectedItem = [model itemAtIndexPath:indexPath];

  // Only search engine items can be selected.
  DCHECK(selectedItem.type == ItemTypePrepopulatedEngine ||
         selectedItem.type == ItemTypeCustomEngine);

  // Do nothing if the tapped engine was already the default.
  if (selectedItem.accessoryType == UITableViewCellAccessoryCheckmark) {
    return;
  }

  // Iterate through the engines and remove the checkmark from any that have it.
  if ([model hasSectionForSectionIdentifier:SectionIdentifierFirstList]) {
    for (TableViewItem* item in
         [model itemsInSectionWithIdentifier:SectionIdentifierFirstList]) {
      if (item.accessoryType == UITableViewCellAccessoryCheckmark) {
        item.accessoryType = UITableViewCellAccessoryNone;
        UITableViewCell* cell =
            [tableView cellForRowAtIndexPath:[model indexPathForItem:item]];
        cell.accessoryType = UITableViewCellAccessoryNone;
      }
    }
  }
  if ([model hasSectionForSectionIdentifier:SectionIdentifierSecondList]) {
    for (TableViewItem* item in
         [model itemsInSectionWithIdentifier:SectionIdentifierSecondList]) {
      DCHECK(item.type == ItemTypeCustomEngine);
      if (item.accessoryType == UITableViewCellAccessoryCheckmark) {
        item.accessoryType = UITableViewCellAccessoryNone;
        UITableViewCell* cell =
            [tableView cellForRowAtIndexPath:[model indexPathForItem:item]];
        cell.accessoryType = UITableViewCellAccessoryNone;
      }
    }
  }

  // Show the checkmark on the new default engine.
  TableViewItem* newDefaultEngine = [model itemAtIndexPath:indexPath];
  newDefaultEngine.accessoryType = UITableViewCellAccessoryCheckmark;
  UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
  cell.accessoryType = UITableViewCellAccessoryCheckmark;

  // Set the new engine as the default.
  self.updatingBackend = YES;
  if (indexPath.section ==
      [model sectionForSectionIdentifier:SectionIdentifierFirstList]) {
    _templateURLService->SetUserSelectedDefaultSearchProvider(
        _firstList[indexPath.row],
        search_engines::ChoiceMadeLocation::kSearchEngineSettings);
  } else {
    _templateURLService->SetUserSelectedDefaultSearchProvider(
        _secondList[indexPath.row],
        search_engines::ChoiceMadeLocation::kSearchEngineSettings);
  }
  [self recordUmaOfDefaultSearchEngine];
  self.updatingBackend = NO;
}

- (void)tableView:(UITableView*)tableView
    didDeselectRowAtIndexPath:(NSIndexPath*)indexPath {
  [super tableView:tableView didDeselectRowAtIndexPath:indexPath];
  if (!self.tableView.editing) {
    return;
  }

  if (self.tableView.indexPathsForSelectedRows.count == 0) {
    self.deleteButton.enabled = NO;
  }
}

#pragma mark - UITableViewDataSource

- (BOOL)tableView:(UITableView*)tableView
    canEditRowAtIndexPath:(NSIndexPath*)indexPath {
  // Only the search engines from the second section can be removed.
  // In the first section, search engines are either prepopulated or
  // selected custom search engines.
  TableViewModel* model = self.tableViewModel;
  NSInteger sectionIdentifier =
      [model sectionIdentifierForSectionIndex:indexPath.section];
  return sectionIdentifier == SectionIdentifierSecondList;
}

- (void)tableView:(UITableView*)tableView
    commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
     forRowAtIndexPath:(NSIndexPath*)indexPath {
  DCHECK(editingStyle == UITableViewCellEditingStyleDelete);
  [self deleteItemAtIndexPaths:@[ indexPath ]];
}

#pragma mark - SearchEngineObserving

- (void)searchEngineChanged {
  if (!self.updatingBackend) {
    [self reloadData];
  } else {
    self.searchEngineChangedInBackground = YES;
  }
}

#pragma mark - Private methods

// Loads the separated TemplateURLs from TemplateURLService and populates the
// `_firstList` and `_secondList`:
// * `_firstList`: Prepopulated engines, engines created by policy, default
// search engine
// * `_secondList`: Other recently visited TemplateURLs
- (void)loadSearchEngines {
  if (_settingsAreDismissed) {
    return;
  }

  if (base::FeatureList::IsEnabled(switches::kSearchSettingsUpdateV2)) {
    // Retrieve separated URLs from the service.
    TemplateURLService::PrepopulatedAndRecentlyVisitedTemplateUrls urls =
        _templateURLService->GetPrepopulatedAndRecentlyVisitedTemplateURLs();

    _firstList = std::move(urls.prepopulated_urls);
    _secondList = std::move(urls.recently_visited_urls);
    return;
  }

  std::vector<raw_ptr<TemplateURL, VectorExperimental>> urls =
      _templateURLService->GetTemplateURLs();
  _firstList.clear();
  _firstList.reserve(urls.size());
  _secondList.clear();
  _secondList.reserve(urls.size());

  // Classify TemplateURLs.
  for (TemplateURL* url : urls) {
    // Starter pack is not supported on iOS.
    if (url->starter_pack_id() !=
        template_url_starter_pack_data::StarterPackId::kNone) {
      continue;
    }

    if ([self isPrepopulatedOrDefaultSearchEngine:url]) {
      _firstList.push_back(url);
    } else {
      _secondList.push_back(url);
    }
  }

  // Do not sort prepopulated search engines, they are already sorted by
  // locale use.

  // Partially sort `_secondList` by TemplateURL's last_visited time.
  auto begin = _secondList.begin();
  auto end = _secondList.end();
  auto pivot = begin + std::min(kMaxcustomSearchEngines, _secondList.size());
  std::partial_sort(begin, pivot, end,
                    [](const TemplateURL* lhs, const TemplateURL* rhs) {
                      return lhs->last_visited() > rhs->last_visited();
                    });

  // Keep the search engines visited within `kMaxVisitAge` and erase others.
  auto cutBegin = std::ranges::lower_bound(
      begin, pivot, base::Time::Now() - kMaxVisitAge,
      std::ranges::greater_equal(), &TemplateURL::last_visited);
  _secondList.erase(cutBegin, end);
}

// Creates a SettingsSearchEngineItem for `templateURL`.
- (TableViewItem*)createSettingsSearchEngineItemFromTemplateURL:
    (const TemplateURL*)templateURL {
  if (_settingsAreDismissed) {
    return nil;
  }

  SettingsSearchEngineItem* item = nil;
  if (templateURL->prepopulate_id() > 0) {
    item = [[SettingsSearchEngineItem alloc]
        initWithType:ItemTypePrepopulatedEngine];
  } else {
    item = [[SettingsSearchEngineItem alloc] initWithType:ItemTypeCustomEngine];
  }
  item.templateURL = templateURL;
  item.text = base::SysUTF16ToNSString(templateURL->short_name());
  item.detailText = base::SysUTF16ToNSString(templateURL->keyword());
  if ([self isItem:item
          equalForTemplateURL:_templateURLService
                                  ->GetDefaultSearchProvider()]) {
    [item setAccessoryType:UITableViewCellAccessoryCheckmark];
  }
  __weak __typeof(self) weakSelf = self;
  GetSearchEngineFavicon(
      *templateURL, *_regionalCapabilitiesService, _templateURLService,
      _faviconLoader, ^(FaviconAttributes* attributes, bool cached) {
        [weakSelf faviconReceivedFor:item faviconAttributes:attributes];
      });
  return item;
}

- (void)faviconReceivedFor:(SettingsSearchEngineItem*)item
         faviconAttributes:(FaviconAttributes*)attributes {
  item.faviconAttributes = attributes;
  [self reconfigureCellsForItems:@[ item ]];
}

// Records the type of the selected default search engine.
- (void)recordUmaOfDefaultSearchEngine {
  UMA_HISTOGRAM_ENUMERATION(
      kUmaSelectDefaultSearchEngine,
      _templateURLService->GetDefaultSearchProvider()->GetEngineType(
          _templateURLService->search_terms_data()),
      SEARCH_ENGINE_MAX);
}

// Deletes custom search engines at `indexPaths`.
// The selected custom search engine cannot be removed.
- (void)deleteItemAtIndexPaths:(NSArray<NSIndexPath*>*)indexPaths {
  if (_settingsAreDismissed) {
    return;
  }
  // Update `_templateURLService` and `_secondList`.
  _updatingBackend = YES;
  NSInteger secondSectionIdentifier = [self.tableViewModel
      sectionForSectionIdentifier:SectionIdentifierSecondList];

  // Remove search engines from `_templateURLService` and `_secondList`.
  for (NSIndexPath* path : indexPaths) {
    TableViewItem* item = [self.tableViewModel itemAtIndexPath:path];
    SettingsSearchEngineItem* engineItem =
        base::apple::ObjCCastStrict<SettingsSearchEngineItem>(item);
    // It should not be possible to remove a search engine from the first
    // section. The settings should either contains a selected custom search
    // engine (which cannot be removed as long as it is selected),
    // or prepopulated search engine.
    CHECK_EQ(path.section, secondSectionIdentifier);
    std::erase(_secondList, engineItem.templateURL);
    _templateURLService->Remove(engineItem.templateURL);
  }

  // Update UI.
  __weak SearchEngineTableViewController* weakSelf = self;
  [self.tableView
      performBatchUpdates:^{
        SearchEngineTableViewController* strongSelf = weakSelf;
        if (!strongSelf) {
          return;
        }

        TableViewModel* model = strongSelf.tableViewModel;
        [strongSelf removeFromModelItemAtIndexPaths:indexPaths];
        [strongSelf.tableView
            deleteRowsAtIndexPaths:indexPaths
                  withRowAnimation:UITableViewRowAnimationAutomatic];

        // Remove second section if it's empty.
        if (strongSelf->_secondList.empty() &&
            [model
                hasSectionForSectionIdentifier:SectionIdentifierSecondList]) {
          NSInteger section =
              [model sectionForSectionIdentifier:SectionIdentifierSecondList];
          [model removeSectionWithIdentifier:SectionIdentifierSecondList];
          [strongSelf.tableView
                deleteSections:[NSIndexSet indexSetWithIndex:section]
              withRowAnimation:UITableViewRowAnimationFade];
        }

        _updatingBackend = NO;
      }
      completion:^(BOOL finished) {
        SearchEngineTableViewController* strongSelf = weakSelf;
        if (!strongSelf) {
          return;
        }

        // Update editing status.
        if (![strongSelf editButtonEnabled]) {
          [strongSelf setEditing:NO animated:YES];
        }
        [strongSelf updateUIForEditState];
      }];
}

// Disables prepopulated engines and removes the checkmark in editing mode.
// Enables engines and recovers the checkmark in normal mode.
- (void)updatePrepopulatedEnginesForEditing:(BOOL)editing {
  if (_settingsAreDismissed) {
    return;
  }
  // All prepopulated items in the first section should be updated.
  // The selected custom item should be updated since the user is not allowed
  // to remove it.
  NSArray<TableViewItem*>* items = [self.tableViewModel
      itemsInSectionWithIdentifier:SectionIdentifierFirstList];
  for (TableViewItem* item in items) {
    SettingsSearchEngineItem* engineItem =
        base::apple::ObjCCastStrict<SettingsSearchEngineItem>(item);
    engineItem.enabled = !editing;
    if (!editing && [self isItem:engineItem
                        equalForTemplateURL:_templateURLService
                                                ->GetDefaultSearchProvider()]) {
      engineItem.accessoryType = UITableViewCellAccessoryCheckmark;
    } else {
      engineItem.accessoryType = UITableViewCellAccessoryNone;
    }
  }
  [self reloadCellsForItems:items
           withRowAnimation:UITableViewRowAnimationAutomatic];
}

// Returns whether the `item` is the same as an item that would be created
// from `templateURL`.
- (BOOL)isItem:(SettingsSearchEngineItem*)item
    equalForTemplateURL:(const TemplateURL*)templateURL {
  if (!templateURL) {
    return NO;
  }
  NSString* name = base::SysUTF16ToNSString(templateURL->short_name());
  NSString* keyword = base::SysUTF16ToNSString(templateURL->keyword());
  return [item.text isEqualToString:name] &&
         [item.detailText isEqualToString:keyword];
}

- (BOOL)isPrepopulatedOrDefaultSearchEngine:(const TemplateURL*)templateURL {
  return _templateURLService->IsPrepopulatedOrDefaultProviderByPolicy(
             templateURL) ||
         templateURL == _templateURLService->GetDefaultSearchProvider();
}

@end
