// Copyright 2016 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/test/app/tab_test_util.h"

#import <Foundation/Foundation.h>

#import "base/apple/foundation_util.h"
#import "ios/chrome/app/main_controller.h"
#import "ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.h"
#import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h"
#import "ios/chrome/browser/scene/coordinator/scene_coordinator.h"
#import "ios/chrome/browser/sessions/model/session_restoration_service.h"
#import "ios/chrome/browser/sessions/model/session_restoration_service_factory.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_controller_testing.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/public/commands/browser_commands.h"
#import "ios/chrome/browser/shared/public/commands/open_new_tab_command.h"
#import "ios/chrome/browser/shared/public/commands/scene_commands.h"
#import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
#import "ios/chrome/browser/shared/public/features/system_flags.h"
#import "ios/chrome/browser/tabs/model/tab_title_util.h"
#import "ios/chrome/browser/url_loading/model/url_loading_params.h"
#import "ios/chrome/browser/web_state_list/model/web_usage_enabler/web_usage_enabler_browser_agent.h"
#import "ios/chrome/test/app/chrome_test_util.h"
#import "ios/testing/open_url_context.h"

namespace chrome_test_util {

namespace {

// Returns the WebStateList for the current mode. Or nullptr of there is no
// browser.
WebStateList* GetCurrentWebStateList() {
  Browser* browser = GetCurrentBrowser();
  return browser ? browser->GetWebStateList() : nullptr;
}

// Close all tabs for `browser` and request the session to be saved.
void CloseAllTabsForBrowser(Browser* browser) {
  DCHECK(browser);
  const auto close_reason = WebStateList::ClosingReason::kUserAction;
  CloseAllWebStates(*browser->GetWebStateList(), close_reason);
  ProfileIOS* profile = browser->GetProfile();
  SessionRestorationServiceFactory::GetForProfile(profile)->SaveSessions();
}

}  // namespace

BOOL IsIncognitoMode() {
  return GetForegroundActiveScene()
      .browserProviderInterface.currentBrowserProvider.browser->GetProfile()
      ->IsOffTheRecord();
}

void OpenNewTab() {
  @autoreleasepool {  // Make sure that all internals are deallocated.
    OpenNewTabCommand* command = [OpenNewTabCommand command];
    if (GetForegroundActiveSceneController().mainCoordinator.isTabGridActive) {
      // The TabGrid is currently presented.
      UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kChromeUINewTabURL));
      [GetForegroundActiveSceneController()
          addANewTabAndPresentBrowser:GetMainBrowser()
                    withURLLoadParams:params];
      return;
    }
    id<SceneCommands> handler = chrome_test_util::HandlerForActiveBrowser();
    [handler openURLInNewTab:command];
  }
}

void SimulateExternalAppURLOpeningWithURL(NSURL* URL) {
  TestOpenURLContext* context = [[TestOpenURLContext alloc] init];
  context.URL = URL;

  UIApplication* application = UIApplication.sharedApplication;
  UIScene* scene = application.connectedScenes.anyObject;
  [scene.delegate scene:scene openURLContexts:[NSSet setWithObject:context]];
}

void OpenNewIncognitoTab() {
  @autoreleasepool {  // Make sure that all internals are deallocated.
    OpenNewTabCommand* command = [OpenNewTabCommand incognitoTabCommand];
    if (GetForegroundActiveSceneController().mainCoordinator.isTabGridActive) {
      // The TabGrid is currently presented.
      Browser* browser =
          GetForegroundActiveScene()
              .browserProviderInterface.incognitoBrowserProvider.browser;
      UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kChromeUINewTabURL));
      [GetForegroundActiveSceneController() addANewTabAndPresentBrowser:browser
                                                      withURLLoadParams:params];
      return;
    }
    id<SceneCommands> handler = chrome_test_util::HandlerForActiveBrowser();
    [handler openURLInNewTab:command];
  }
}

web::WebState* GetCurrentWebState() {
  WebStateList* web_state_list = GetCurrentWebStateList();
  if (!web_state_list) {
    return nullptr;
  }
  web::WebState* current_web_state = web_state_list->GetActiveWebState();
  if (!current_web_state) {
    return nullptr;
  }

  // If there is a Reading Mode web state enabled on the page, run tests on the
  // content that is displayed here.
  ReaderModeTabHelper* reader_mode_tab_helper =
      ReaderModeTabHelper::FromWebState(current_web_state);
  if (reader_mode_tab_helper) {
    return reader_mode_tab_helper->GetReaderModeWebState() ?: current_web_state;
  }
  return current_web_state;
}

web::WebState* GetNextWebState() {
  WebStateList* web_state_list = GetCurrentWebStateList();
  if (!web_state_list || web_state_list->count() < 2) {
    return nullptr;
  }
  int next_index = web_state_list->active_index() + 1;
  if (next_index >= web_state_list->count()) {
    next_index = 0;
  }
  return web_state_list->GetWebStateAt(next_index);
}

web::WebState* GetWebStateAtIndexInCurrentMode(int index) {
  WebStateList* web_state_list = GetCurrentWebStateList();
  if (!web_state_list || !web_state_list->ContainsIndex(index)) {
    return nullptr;
  }
  return web_state_list->GetWebStateAt(index);
}

NSString* GetCurrentTabTitle() {
  return tab_util::GetTabTitle(GetCurrentWebState());
}

NSString* GetNextTabTitle() {
  return tab_util::GetTabTitle(GetNextWebState());
}

void CloseCurrentTab() {
  WebStateList* web_state_list = GetCurrentWebStateList();
  if (!web_state_list ||
      web_state_list->active_index() == WebStateList::kInvalidIndex) {
    return;
  }
  web_state_list->CloseWebStateAt(web_state_list->active_index(),
                                  WebStateList::ClosingReason::kUserAction);
}

void PinCurrentTab() {
  WebStateList* web_state_list = GetCurrentWebStateList();
  if (!web_state_list ||
      web_state_list->active_index() == WebStateList::kInvalidIndex) {
    return;
  }
  web_state_list->SetWebStatePinnedAt(web_state_list->active_index(),
                                      /*pinned =*/true);
}

void CloseTabAtIndex(NSUInteger index) {
  @autoreleasepool {  // Make sure that all internals are deallocated.
    DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
    GetCurrentWebStateList()->CloseWebStateAt(
        static_cast<int>(index), WebStateList::ClosingReason::kUserAction);
  }
}

NSUInteger GetIndexOfActiveNormalTab() {
  return GetMainBrowser()->GetWebStateList()->active_index();
}

void CloseAllTabsInCurrentMode() {
  CloseAllWebStates(*GetCurrentWebStateList(),
                    WebStateList::ClosingReason::kUserAction);
}

void CloseAllTabs() {
  if (GetIncognitoTabCount() && GetForegroundActiveSceneController()) {
    CloseAllTabsForBrowser(
        GetForegroundActiveScene()
            .browserProviderInterface.incognitoBrowserProvider.browser);
  }
  if (GetMainTabCount() && GetForegroundActiveScene()) {
    CloseAllTabsForBrowser(GetMainBrowser());
  }
  if (GetInactiveTabCount() && GetForegroundActiveScene()) {
    CloseAllTabsForBrowser(GetForegroundActiveScene()
                               .browserProviderInterface.mainBrowserProvider
                               .browser->GetInactiveBrowser());
  }
}

void SelectTabAtIndexInCurrentMode(NSUInteger index) {
  @autoreleasepool {  // Make sure that all internals are deallocated.

    WebStateList* web_state_list = GetCurrentWebStateList();
    web_state_list->ActivateWebStateAt(static_cast<int>(index));
  }
}

NSUInteger GetMainTabCount() {
  return GetMainBrowser() ? GetMainBrowser()->GetWebStateList()->count() : 0;
}

NSUInteger GetInactiveTabCount() {
  return GetForegroundActiveScene()
      .browserProviderInterface.mainBrowserProvider.browser
      ->GetInactiveBrowser()
      ->GetWebStateList()
      ->count();
}

NSUInteger GetIncognitoTabCount() {
  return GetForegroundActiveScene()
      .browserProviderInterface.incognitoBrowserProvider.browser
      ->GetWebStateList()
      ->count();
}

BOOL ResetTabUsageRecorder() {
  TabUsageRecorderBrowserAgent* tab_usage_recorder =
      TabUsageRecorderBrowserAgent::FromBrowser(GetCurrentBrowser());
  if (!tab_usage_recorder) {
    return NO;
  }
  tab_usage_recorder->ResetAll();
  return YES;
}

BOOL SetCurrentTabsToBeColdStartTabs() {
  TabUsageRecorderBrowserAgent* tab_usage_recorder =
      TabUsageRecorderBrowserAgent::FromBrowser(GetCurrentBrowser());

  if (!tab_usage_recorder) {
    return NO;
  }
  WebStateList* web_state_list = GetCurrentWebStateList();

  std::vector<web::WebState*> web_states;
  web_states.reserve(web_state_list->count());
  for (int index = 0; index < web_state_list->count(); ++index) {
    web_states.push_back(web_state_list->GetWebStateAt(index));
  }
  tab_usage_recorder->InitialRestoredTabs(web_state_list->GetActiveWebState(),
                                          web_states);
  return YES;
}

BOOL SimulateTabsBackgrounding() {
  TabUsageRecorderBrowserAgent* tab_usage_recorder =
      TabUsageRecorderBrowserAgent::FromBrowser(GetCurrentBrowser());
  if (!tab_usage_recorder) {
    return NO;
  }
  tab_usage_recorder->AppDidEnterBackground();
  return YES;
}

void EvictOtherBrowserTabs() {
  id<BrowserProviderInterface> provider =
      GetForegroundActiveScene().browserProviderInterface;
  Browser* otherBrowser = IsIncognitoMode()
                              ? provider.mainBrowserProvider.browser
                              : provider.incognitoBrowserProvider.browser;
  // Disabling and enabling web usage will evict all web views.
  WebUsageEnablerBrowserAgent* enabler =
      WebUsageEnablerBrowserAgent::FromBrowser(otherBrowser);
  DCHECK(enabler);
  enabler->SetWebUsageEnabled(false);
  enabler->SetWebUsageEnabled(true);
}

BOOL CloseAllNormalTabs() {
  Browser* browser = GetMainBrowser();
  DCHECK(browser);
  CloseAllWebStates(*browser->GetWebStateList(),
                    WebStateList::ClosingReason::kUserAction);
  return YES;
}

BOOL CloseAllIncognitoTabs() {
  SceneState* scene_state = GetForegroundActiveScene();
  DCHECK(scene_state);

  Browser* browser =
      scene_state.browserProviderInterface.incognitoBrowserProvider.browser;
  DCHECK(browser);
  CloseAllWebStates(*browser->GetWebStateList(),
                    WebStateList::ClosingReason::kUserAction);
  return YES;
}

NSUInteger GetEvictedMainTabCount() {
  TabUsageRecorderBrowserAgent* tab_usage_recorder =
      TabUsageRecorderBrowserAgent::FromBrowser(GetMainBrowser());
  if (!tab_usage_recorder) {
    return 0;
  }
  return tab_usage_recorder->EvictedTabsMapSize();
}

}  // namespace chrome_test_util
