// Copyright 2020 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/menu/ui_bundled/action_factory.h"

#import "base/check.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/user_metrics.h"
#import "base/strings/sys_string_conversions.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/menu/ui_bundled/menu_action_type.h"
#import "ios/chrome/browser/net/model/crurl.h"
#import "ios/chrome/browser/shared/model/web_state_list/tab_group.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/scene_commands.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/util/color_palette/tab_group_color_palette.h"
#import "ios/chrome/browser/shared/ui/util/pasteboard_util.h"
#import "ios/chrome/browser/signin/model/system_identity.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/public/provider/chrome/browser/context_menu/context_menu_api.h"
#import "ui/base/l10n/l10n_util_mac.h"
#import "url/gurl.h"

namespace {

// The emoji that is drawn into a UIImage for the Gemini entry point action.
constexpr NSString* kGeminiActionImageEmoji = @"🍌";

// The ratio of canvas to font point size to allow for the canvas to have some
// padding around the emoji, which fixes clipping.
constexpr CGFloat kEmojiCanvasPaddingRatio = 1.3;

}  // namespace

@interface ActionFactory ()

// Histogram to record executed actions.
@property(nonatomic, assign) const char* histogram;

@end

@implementation ActionFactory

- (instancetype)initWithScenario:(MenuScenarioHistogram)scenario {
  if ((self = [super init])) {
    _histogram = GetActionsHistogramName(scenario);
  }
  return self;
}

- (UIAction*)actionWithTitle:(NSString*)title
                       image:(UIImage*)image
                        type:(MenuActionType)type
                       block:(ProceduralBlock)block {
  // Capture only the histogram name's pointer to be copied by the block.
  const char* histogram = self.histogram;
  return [UIAction actionWithTitle:title
                             image:image
                        identifier:nil
                           handler:^(UIAction* action) {
                             base::UmaHistogramEnumeration(histogram, type);
                             if (block) {
                               block();
                             }
                           }];
}

- (UIAction*)actionToCopyURL:(CrURL*)URL {
  return [self actionToCopyURLWithBlock:^{
    StoreURLInPasteboard(URL.gurl);
  }];
}

- (UIAction*)actionToCopyURLWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolLinkAction, kSymbolActionPointSize);
  return [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_COPY_LINK_ACTION_TITLE)
                image:image
                 type:MenuActionType::CopyURL
                block:block];
}

- (UIAction*)actionToShowFullURL:(NSString*)URLString
                           block:(ProceduralBlock)block {
  UIAction* action = [self actionWithTitle:nil
                                     image:nil
                                      type:MenuActionType::ShowFullURL
                                     block:block];
  action.accessibilityLabel =
      l10n_util::GetNSString(IDS_IOS_SHOW_FULL_URL_BUTTON_ACCESSIBILITY_LABEL);
  action.attributes = UIMenuElementAttributesKeepsMenuPresented;
  action.subtitle = ios::provider::StyledContextMenuStringForString(URLString);
  return action;
}

- (UIAction*)actionToShareWithBlock:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolShare, kSymbolActionPointSize);
  return
      [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_SHARE_BUTTON_LABEL)
                      image:image
                       type:MenuActionType::Share
                      block:block];
}

- (UIAction*)actionToPinTabWithBlock:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolPin, kSymbolActionPointSize);
  return [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_PINTAB)
                image:image
                 type:MenuActionType::PinTab
                block:block];
}

- (UIAction*)actionToUnpinTabWithBlock:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolPinSlash, kSymbolActionPointSize);
  return [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_UNPINTAB)
                image:image
                 type:MenuActionType::UnpinTab
                block:block];
}

- (UIAction*)actionToDeleteWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolDeleteAction, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_DELETE_ACTION_TITLE)
                      image:image
                       type:MenuActionType::Delete
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToOpenInNewTabWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolNewTabAction, kSymbolActionPointSize);
  ProceduralBlock completionBlock =
      [self recordMobileWebContextMenuOpenTabActionWithBlock:block];

  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB)
                         image:image
                          type:MenuActionType::OpenInNewTab
                         block:completionBlock];
}

- (UIAction*)actionToOpenAllTabsWithBlock:(ProceduralBlock)block {
  return [self
      actionWithTitle:l10n_util::GetNSString(
                          IDS_IOS_CONTENT_CONTEXT_OPEN_ALL_LINKS)
                image:SymbolWithPointSize(SymbolPlus, kSymbolActionPointSize)
                 type:MenuActionType::OpenAllInNewTabs
                block:block];
}

- (UIAction*)actionToRemoveWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolHideAction, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_SUGGESTIONS_NEVER_SHOW_SITE)
                      image:image
                       type:MenuActionType::Remove
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToEditWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolEditAction, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_EDIT_ACTION_TITLE)
                         image:image
                          type:MenuActionType::Edit
                         block:block];
}

- (UIAction*)actionToHideWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolHideAction, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_RECENT_TABS_HIDE_MENU_OPTION)
                      image:image
                       type:MenuActionType::Hide
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToMoveFolderWithBlock:(ProceduralBlock)block {
  // Use multi color to make sure the arrow is visible.
  UIImage* image = MakeSymbolMulticolor(
      SymbolWithPointSize(SymbolMoveFolder, kSymbolActionPointSize));
  return [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_MENU_MOVE)
                image:image
                 type:MenuActionType::Move
                block:block];
}

- (UIAction*)actionToMarkAsReadWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolMarkAsReadAction, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_READING_LIST_MARK_AS_READ_ACTION)
                         image:image
                          type:MenuActionType::Read
                         block:block];
}

- (UIAction*)actionToMarkAsUnreadWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolMarkAsUnreadAction, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_READING_LIST_MARK_AS_UNREAD_ACTION)
                         image:image
                          type:MenuActionType::Unread
                         block:block];
}

- (UIAction*)actionToOpenOfflineVersionInNewTabWithBlock:
    (ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolCheckmarkCircle, kSymbolActionPointSize);
  ProceduralBlock completionBlock =
      [self recordMobileWebContextMenuOpenTabActionWithBlock:block];

  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_READING_LIST_OPEN_OFFLINE_BUTTON)
                         image:image
                          type:MenuActionType::ViewOffline
                         block:completionBlock];
}

- (UIAction*)actionToAddToReadingListWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolReadLaterAction, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_CONTEXT_ADDTOREADINGLIST)
                         image:image
                          type:MenuActionType::AddToReadingList
                         block:block];
}

- (UIAction*)actionToBookmarkWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolAddBookmarkAction, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_CONTEXT_ADDTOBOOKMARKS)
                         image:image
                          type:MenuActionType::AddToBookmarks
                         block:block];
}

- (UIAction*)actionToEditBookmarkWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolEditAction, kSymbolActionPointSize);
  return [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_MENU_EDIT)
                image:image
                 type:MenuActionType::EditBookmark
                block:block];
}

- (UIAction*)actionToCloseRegularTabWithBlock:(ProceduralBlock)block {
  NSString* title = l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_CLOSETAB);
  return [self actionToCloseTabWithTitle:title block:block];
}

- (UIAction*)actionToClosePinnedTabWithBlock:(ProceduralBlock)block {
  NSString* title =
      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_CLOSEPINNEDTAB);
  return [self actionToCloseTabWithTitle:title block:block];
}

- (UIAction*)actionToCloseAllOtherTabsWithBlock:(ProceduralBlock)block {
  NSString* title =
      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_CLOSEOTHERTABS);
  UIImage* image = SymbolWithPointSize(SymbolXMark, kSymbolActionPointSize);
  UIAction* action = [self actionWithTitle:title
                                     image:image
                                      type:MenuActionType::CloseAllOtherTabs
                                     block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToDeleteBrowsingDataWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolDeleteAction, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_TOOLS_MENU_CLEAR_BROWSING_DATA)
                      image:image
                       type:MenuActionType::DeleteBrowsingData
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionSaveImageWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolSaveImageAction, kSymbolActionPointSize);
  UIAction* action = [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_SAVEIMAGE)
                image:image
                 type:MenuActionType::SaveImage
                block:block];
  return action;
}

- (UIAction*)actionCopyImageWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolCopyAction, kSymbolActionPointSize);
  UIAction* action = [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_COPYIMAGE)
                image:image
                 type:MenuActionType::CopyImage
                block:block];
  return action;
}

- (UIAction*)actionSearchImageWithTitle:(NSString*)title
                                  Block:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolPhotoBadgeMagnifyingglass,
                                       kSymbolActionPointSize);
  UIAction* action = [self actionWithTitle:title
                                     image:image
                                      type:MenuActionType::SearchImage
                                     block:block];
  return action;
}

- (UIAction*)actionToCloseAllTabsWithBlock:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolXMark, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_CONTEXT_CLOSEALLTABSANDGROUPS)
                      image:image
                       type:MenuActionType::CloseAllTabs
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToSelectTabsWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolCheckmarkCircle, kSymbolActionPointSize);
  UIAction* action = [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_SELECTTABS)
                image:image
                 type:MenuActionType::SelectTabs
                block:block];
  return action;
}

- (UIAction*)actionToSearchImageUsingLensWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolCameraLens, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTEXT_MENU_SEARCHIMAGEWITHGOOGLE)
                      image:image
                       type:MenuActionType::SearchImageWithLens
                      block:block];
  return action;
}

- (ProceduralBlock)recordMobileWebContextMenuOpenTabActionWithBlock:
    (ProceduralBlock)block {
  return ^{
    base::RecordAction(base::UserMetricsAction("MobileWebContextMenuOpenTab"));
    if (block) {
      block();
    }
  };
}

- (UIAction*)actionToCreateEmptyTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolNewTabGroupAction, kSymbolActionPointSize);
  UIAction* action = [self
      actionWithTitle:l10n_util::GetNSString(
                          IDS_IOS_CONTENT_CONTEXT_ADDTABTONEWTABGROUP_SUBMENU)
                image:image
                 type:MenuActionType::CreateEmptyTabGroup
                block:block];
  return action;
}

- (UIAction*)actionToAddTabsToNewGroupWithTabsNumber:(int)tabsNumber
                                           inSubmenu:(BOOL)inSubmenu
                                               block:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolNewTabGroupAction, kSymbolActionPointSize);
  NSString* title =
      inSubmenu ? l10n_util::GetNSString(
                      IDS_IOS_CONTENT_CONTEXT_ADDTABTONEWTABGROUP_SUBMENU)
                : l10n_util::GetPluralNSStringF(
                      IDS_IOS_CONTENT_CONTEXT_ADDTABTONEWTABGROUP, tabsNumber);
  UIAction* action = [self actionWithTitle:title
                                     image:image
                                      type:MenuActionType::AddTabToNewGroup
                                     block:block];
  return action;
}

- (UIAction*)actionToOpenLinkInNewGroupWithBlock:(ProceduralBlock)block
                                       inSubmenu:(BOOL)inSubmenu {
  UIImage* image =
      SymbolWithPointSize(SymbolNewTabGroupAction, kSymbolActionPointSize);
  NSString* title =
      inSubmenu ? l10n_util::GetNSString(
                      IDS_IOS_CONTENT_CONTEXT_ADDTABTONEWTABGROUP_SUBMENU)
                : l10n_util::GetNSString(
                      IDS_IOS_CONTENT_CONTEXT_OPENLINKINNEWTABGROUP);
  UIAction* action = [self actionWithTitle:title
                                     image:image
                                      type:MenuActionType::OpenLinkInNewGroup
                                     block:block];
  return action;
}

- (UIMenuElement*)
    menuToAddTabToGroupWithGroups:(const std::set<const TabGroup*>&)groups
                     numberOfTabs:(int)tabsNumber
                            block:(void (^)(const TabGroup*))block {
  if (groups.size() == 0) {
    ProceduralBlock addTabToNewGroupBlock = ^{
      if (block) {
        block(nil);
      }
    };
    return [self actionToAddTabsToNewGroupWithTabsNumber:tabsNumber
                                               inSubmenu:NO
                                                   block:addTabToNewGroupBlock];
  }

  NSArray<UIMenuElement*>* groupsMenu = [self groupsMenuForGroups:groups
                                                     currentGroup:nil
                                                            block:block];
  UIMenu* menu = [UIMenu menuWithTitle:@""
                                 image:nil
                            identifier:nil
                               options:UIMenuOptionsDisplayInline
                              children:groupsMenu];
  ProceduralBlock addTabToNewGroupBlock = ^{
    if (block) {
      block(nil);
    }
  };
  NSArray<UIMenuElement*>* addToGroupMenuElements = @[
    [self actionToAddTabsToNewGroupWithTabsNumber:tabsNumber
                                        inSubmenu:YES
                                            block:addTabToNewGroupBlock],
    menu
  ];

  UIImage* image =
      SymbolWithPointSize(SymbolMoveTabToGroupAction, kSymbolActionPointSize);

  return [UIMenu
      menuWithTitle:l10n_util::GetPluralNSStringF(
                        IDS_IOS_CONTENT_CONTEXT_ADDTABTOTABGROUP, tabsNumber)
              image:image
         identifier:nil
            options:UIMenuOptionsSingleSelection
           children:addToGroupMenuElements];
}

- (UIMenuElement*)
    menuToMoveTabToGroupWithGroups:(const std::set<const TabGroup*>&)groups
                      currentGroup:(const TabGroup*)currentGroup
                         moveBlock:(void (^)(const TabGroup*))moveBlock
                       removeBlock:(ProceduralBlock)removeBlock {
  if (groups.size() == 0) {
    NOTREACHED() << "Groups cannot be empty.";
  }

  NSArray<UIMenuElement*>* groupsMenu = [self groupsMenuForGroups:groups
                                                     currentGroup:currentGroup
                                                            block:moveBlock];
  UIMenu* menu = [UIMenu menuWithTitle:@""
                                 image:nil
                            identifier:nil
                               options:UIMenuOptionsDisplayInline
                              children:groupsMenu];
  NSArray<UIMenuElement*>* moveTabFromGroupMenuElements =
      @[ [self actionToRemoveTabFromGroup:removeBlock], menu ];

  UIImage* image =
      SymbolWithPointSize(SymbolMoveTabToGroupAction, kSymbolActionPointSize);
  return [UIMenu menuWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_CONTEXT_MOVETABTOGROUP)
                         image:image
                    identifier:nil
                       options:UIMenuOptionsSingleSelection
                      children:moveTabFromGroupMenuElements];
}

- (UIMenuElement*)
    menuToOpenLinkInGroupWithGroups:(const std::set<const TabGroup*>&)groups
                              block:(void (^)(const TabGroup*))block {
  if (groups.size() == 0) {
    ProceduralBlock openInNewGroupBlock = ^{
      if (block) {
        block(nil);
      }
    };
    return [self actionToOpenLinkInNewGroupWithBlock:openInNewGroupBlock
                                           inSubmenu:NO];
  }

  NSArray<UIMenuElement*>* groupsMenu = [self groupsMenuForGroups:groups
                                                     currentGroup:nil
                                                            block:block];
  UIMenu* menu = [UIMenu menuWithTitle:@""
                                 image:nil
                            identifier:nil
                               options:UIMenuOptionsDisplayInline
                              children:groupsMenu];
  ProceduralBlock openInNewGroupBlock = ^{
    if (block) {
      block(nil);
    }
  };
  NSArray<UIMenuElement*>* openInGroupMenuElements = @[
    [self actionToOpenLinkInNewGroupWithBlock:openInNewGroupBlock
                                    inSubmenu:YES],
    menu
  ];

  UIImage* image =
      SymbolWithPointSize(SymbolMoveTabToGroupAction, kSymbolActionPointSize);

  return [UIMenu menuWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_CONTEXT_OPENLINKINTABGROUP)
                         image:image
                    identifier:nil
                       options:UIMenuOptionsSingleSelection
                      children:openInGroupMenuElements];
}

- (UIAction*)actionToRenameTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolEditAction, kSymbolActionPointSize);
  UIAction* action = [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_EDITGROUP)
                image:image
                 type:MenuActionType::RenameTabGroup
                block:block];
  return action;
}

- (UIAction*)actionToAddNewTabInGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolNewTabGroupAction, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_CONTEXT_NEWTABINGROUP)
                      image:image
                       type:MenuActionType::NewTabInGroup
                      block:block];
  return action;
}

- (UIAction*)actionToUngroupTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolUngroupTabGroup, kSymbolActionPointSize);
  UIAction* action = [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_UNGROUP)
                image:image
                 type:MenuActionType::UngroupTabGroup
                block:block];
  return action;
}

- (UIAction*)actionToDeleteTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolDeleteAction, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_CONTEXT_DELETEGROUP)
                      image:image
                       type:MenuActionType::DeleteTabGroup
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToCloseTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolXMark, kSymbolActionPointSize);
  UIAction* action = [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_CLOSEGROUP)
                image:image
                 type:MenuActionType::CloseTabGroup
                block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToLeaveSharedTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolMinusInCircle, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_CONTEXT_LEAVESHAREDGROUP)
                      image:image
                       type:MenuActionType::LeaveSharedTabGroup
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToDeleteSharedTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolDeleteAction, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_CONTEXT_DELETESHAREDGROUP)
                      image:image
                       type:MenuActionType::DeleteSharedTabGroup
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToShareTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolPersonPlus, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_CONTEXT_SHARELOCALGROUP)
                      image:image
                       type:MenuActionType::ShareLocalTabGroup
                      block:block];
  return action;
}

- (UIAction*)actionToManageTabGroupWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolPersonPlus, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_CONTEXT_MANAGESHAREDGROUP)
                      image:image
                       type:MenuActionType::ManageSharedTabGroup
                      block:block];
  return action;
}

#pragma mark - Private

// Creates a UIAction instance for closing a tab with a provided `title`.
- (UIAction*)actionToCloseTabWithTitle:(NSString*)title
                                 block:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolXMark, kSymbolActionPointSize);
  UIAction* action = [self actionWithTitle:title
                                     image:image
                                      type:MenuActionType::CloseTab
                                     block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

// Creates a UIAction instance for removing a tab from a group.
- (UIAction*)actionToRemoveTabFromGroup:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolRemoveTabFromGroupAction,
                                       kSymbolActionPointSize);
  NSString* title =
      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_REMOVEFROMGROUP);
  UIAction* action = [self actionWithTitle:title
                                     image:image
                                      type:MenuActionType::RemoveTabFromGroup
                                     block:block];
  return action;
}

- (UIAction*)actionToShowDetailsWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolInfoCircle, kSymbolActionPointSize);
  return [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_SHOW_DETAILS_ACTION_TITLE)
                image:image
                 type:MenuActionType::ShowDetails
                block:block];
}

// Returns an array of group actions for a given set of groups. If
// `currentGroup` is specified and is present in the set, it is selected.
- (NSArray<UIMenuElement*>*)
    groupsMenuForGroups:(const std::set<const TabGroup*>&)groups
           currentGroup:(const TabGroup*)currentGroup
                  block:(void (^)(const TabGroup*))block {
  NSMutableArray<UIMenuElement*>* groupsMenu = [[NSMutableArray alloc] init];

  UIImage* circleImage =
      SymbolWithPointSize(SymbolCircleFill, kSymbolActionPointSize);
  circleImage =
      [circleImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
  for (const TabGroup* group : groups) {
    NSString* title = group->GetTitle();
    base::WeakPtr<const TabGroup> weakGroup = group->GetWeakPtr();
    ProceduralBlock actionBlock = ^{
      if (block) {
        block(weakGroup.get());
      }
    };

    UIColor* imageColor = [TabGroupColorPalette commonColor:group->GetColor()];

    UIAction* groupAction =
        [self actionWithTitle:title
                        image:[circleImage imageWithTintColor:imageColor]
                         type:MenuActionType::MoveTabToExistingGroup
                        block:actionBlock];

    if (group == currentGroup) {
      groupAction.state = UIMenuElementStateOn;
    }
    [groupsMenu addObject:groupAction];
  }
  return groupsMenu;
}

- (UIAction*)actionToSortDriveItemsByNameWithBlock:(ProceduralBlock)block {
  return
      [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_DRIVE_SORT_BY_NAME)
                      image:nil
                       type:MenuActionType::SortDriveItemsByName
                      block:block];
}

- (UIAction*)actionToSortDriveItemsByModificationTimeWithBlock:
    (ProceduralBlock)block {
  return [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_DRIVE_SORT_BY_MODIFICATION)
                image:nil
                 type:MenuActionType::SortDriveItemsByModificationTime
                block:block];
}

- (UIAction*)actionToSortDriveItemsByOpeningTimeWithBlock:
    (ProceduralBlock)block {
  return [self
      actionWithTitle:l10n_util::GetNSString(IDS_IOS_DRIVE_SORT_BY_OPENING)
                image:nil
                 type:MenuActionType::SortDriveItemsByOpeningTime
                block:block];
}

- (UIMenuElement*)
    menuToSelectDriveIdentityWithIdentities:
        (NSArray<id<SystemIdentity>>*)identities
                            currentIdentity:(id<SystemIdentity>)currentIdentity
                                      block:(void (^)(const id<SystemIdentity>))
                                                block {
  NSMutableArray<UIMenuElement*>* identitiesMenuElements =
      [[NSMutableArray alloc] init];
  for (id<SystemIdentity> identity in identities) {
    NSString* email = identity.userEmail;
    ProceduralBlock actionBlock = ^{
      if (block) {
        block(identity);
      }
    };

    UIAction* identityAction =
        [self actionWithTitle:email
                        image:nil
                         type:MenuActionType::SelectDriveIdentity
                        block:actionBlock];
    if (identity == currentIdentity) {
      identityAction.state = UIMenuElementStateOn;
    }
    [identitiesMenuElements addObject:identityAction];
  }

  return [UIMenu menuWithTitle:@""
                         image:nil
                    identifier:nil
                       options:UIMenuOptionsDisplayInline
                      children:identitiesMenuElements];
}

- (UIAction*)actionToAddAccountForDriveWithBlock:(ProceduralBlock)block {
  return [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_DRIVE_ADD_ACCOUNT)
                         image:nil
                          type:MenuActionType::AddDriveAccount
                         block:block];
}

- (UIAction*)actionToShowRecentActivity:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolHistory, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_CONTEXT_RECENTACTIVITY)
                         image:image
                          type:MenuActionType::RecentActivityInSharedTabGroup
                         block:block];
}

- (UIAction*)actionToOpenImageInGeminiWithBlock:(ProceduralBlock)block {
  // Create the canvas slightly bigger than the emoji's text point size, to
  // allow for the parts that overflow.
  CGSize imageSize =
      CGSizeMake(kSymbolActionPointSize * kEmojiCanvasPaddingRatio,
                 kSymbolActionPointSize * kEmojiCanvasPaddingRatio);

  UIGraphicsImageRenderer* renderer =
      [[UIGraphicsImageRenderer alloc] initWithSize:imageSize];

  // Create a UIImage from an emoji.
  UIImage* emojiImage =
      [renderer imageWithActions:^(UIGraphicsImageRendererContext* context) {
        NSDictionary* attrs = @{
          NSFontAttributeName : [UIFont systemFontOfSize:kSymbolActionPointSize]
        };

        // Center the draw point of the emoji in the canvas.
        CGSize textSize = [kGeminiActionImageEmoji sizeWithAttributes:attrs];
        CGPoint drawPoint =
            CGPointMake((imageSize.width - textSize.width) / 2.0,
                        (imageSize.height - textSize.height) / 2.0);

        [kGeminiActionImageEmoji drawAtPoint:drawPoint withAttributes:attrs];
      }];

  return
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_GEMINI_IMAGE_CONTEXT_MENU_ENTRY_POINT)
                      image:emojiImage
                       type:MenuActionType::GeminiWithImageAttachment
                      block:block];
}

- (UIAction*)actionToPinSiteToMostVisitedTileWithBlock:(ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolPin, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_SUGGESTIONS_PIN_SITE)
                         image:image
                          type:MenuActionType::PinSite
                         block:block];
}

- (UIAction*)actionToUnpinSiteFromMostVisitedTileWithBlock:
    (ProceduralBlock)block {
  UIImage* image = SymbolWithPointSize(SymbolPinSlash, kSymbolActionPointSize);
  UIAction* action =
      [self actionWithTitle:l10n_util::GetNSString(
                                IDS_IOS_CONTENT_SUGGESTIONS_UNPIN_SITE)
                      image:image
                       type:MenuActionType::UnpinSite
                      block:block];
  action.attributes = UIMenuElementAttributesDestructive;
  return action;
}

- (UIAction*)actionToEditPinnedSiteOnMostVisitedTileWithBlock:
    (ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolEditAction, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_SUGGESTIONS_EDIT_PINNED_SITE)
                         image:image
                          type:MenuActionType::EditPinnedSite
                         block:block];
}

- (UIAction*)actionToSendTabToSelfWithBlock:(ProceduralBlock)block {
  UIImage* image =
      SymbolWithPointSize(SymbolRecentTabs, kSymbolActionPointSize);
  return [self actionWithTitle:l10n_util::GetNSString(IDS_SEND_TAB_TO_SELF)
                         image:image
                          type:MenuActionType::SendTabToSelf
                         block:block];
}

@end
