// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/browser_tabstrip.h"

#include <optional>

#include "base/command_line.h"
#include "base/feature_list.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/navigator/browser_navigator.h"
#include "chrome/browser/ui/navigator/browser_navigator_params.h"
#include "chrome/browser/ui/tabs/tab_close_types_data.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/window_feature_controller/window_feature_controller.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"

namespace chrome {

GURL GetNewTabURL(const BrowserWindowInterface* browser) {
  if (browser) {
    if (auto* const app_browser_controller =
            web_app::AppBrowserController::From(browser)) {
      return app_browser_controller->GetAppNewTabUrl();
    }
  }
  return ChromeUINewTabURLAsGURL();
}

content::WebContents* AddAndReturnTabAt(
    BrowserWindowInterface* browser,
    const GURL& url,
    int idx,
    bool foreground,
    std::optional<tab_groups::TabGroupId> group,
    bool pinned,
    std::optional<NavigateParams::WindowAction> window_action) {
  const GURL resolved_url = url.is_empty() ? GetNewTabURL(browser) : url;
  NavigateParams params(browser, resolved_url, ui::PAGE_TRANSITION_TYPED);
  params.disposition = foreground ? WindowOpenDisposition::NEW_FOREGROUND_TAB
                                  : WindowOpenDisposition::NEW_BACKGROUND_TAB;
  if (window_action) {
    params.window_action = window_action.value();
  }
  params.tabstrip_index = idx;
  params.group = group;
  if (pinned) {
    params.tabstrip_add_types |= AddTabTypes::ADD_PINNED;
  }

  params.web_app_navigation_data.emplace();
  params.web_app_navigation_data->SetNavigationCapturingForceOff(true);
  Navigate(&params);

  if (!params.navigated_or_inserted_contents) {
    return nullptr;
  }

  return params.navigated_or_inserted_contents;
}

void AddTabAt(BrowserWindowInterface* browser,
              const GURL& url,
              int idx,
              bool foreground,
              std::optional<tab_groups::TabGroupId> group,
              bool pinned,
              std::optional<NavigateParams::WindowAction> window_action) {
  /*void*/ AddAndReturnTabAt(browser, url, idx, foreground, std::move(group),
                             pinned, window_action);
}

content::WebContents* AddSelectedTabWithURL(BrowserWindowInterface* browser,
                                            const GURL& url,
                                            ui::PageTransition transition) {
  NavigateParams params(browser, url, transition);
  params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
  params.web_app_navigation_data.emplace();
  params.web_app_navigation_data->SetNavigationCapturingForceOff(true);
  Navigate(&params);
  return params.navigated_or_inserted_contents;
}

content::WebContents* AddWebContents(
    BrowserWindowInterface* browser,
    content::WebContents* source_contents,
    std::unique_ptr<content::WebContents> new_contents,
    const GURL& target_url,
    WindowOpenDisposition disposition,
    const blink::mojom::WindowFeatures& window_features,
    NavigateParams::WindowAction window_action,
    bool user_gesture) {
  // No code for this yet.
  DCHECK(disposition != WindowOpenDisposition::SAVE_TO_DISK);
  // Can't create a new contents for the current tab - invalid case.
  DCHECK(disposition != WindowOpenDisposition::CURRENT_TAB);

  NavigateParams params(browser, std::move(new_contents));
  params.source_contents = source_contents;
  params.url = target_url;
  params.disposition = disposition;
  params.window_features = window_features;
  params.window_action = window_action;
  // At this point, we're already beyond the popup blocker. Even if the popup
  // was created without a user gesture, we have to set |user_gesture| to true,
  // so it gets correctly focused.
  params.user_gesture = true;
  params.original_user_gesture = user_gesture;

  ConfigureTabGroupForNavigation(&params);

  Navigate(&params);
  return params.navigated_or_inserted_contents;
}

void CloseWebContents(BrowserWindowInterface* browser,
                      content::WebContents* contents,
                      bool add_to_history) {
  int index = browser->GetTabStripModel()->GetIndexOfWebContents(contents);
  if (index == TabStripModel::kNoTab) {
    DUMP_WILL_BE_NOTREACHED()
        << "CloseWebContents called for tab not in our strip";
    return;
  }

  uint32_t close_types = TabCloseTypes::CLOSE_NONE;
  if (auto* data = TabCloseTypesData::FromWebContents(contents)) {
    close_types = data->close_types();
    contents->RemoveUserData(TabCloseTypesData::UserDataKey());
  }

  if (add_to_history) {
    close_types |= TabCloseTypes::CLOSE_CREATE_HISTORICAL_TAB;
  }

  browser->GetTabStripModel()->CloseWebContents(contents, close_types);
}

void ConfigureTabGroupForNavigation(NavigateParams* nav_params) {
  if (!nav_params->source_contents) {
    return;
  }

  if (!nav_params->browser ||
      !WindowFeatureController::From(nav_params->browser)
           ->SupportsWindowFeature(
               WindowFeatureController::WindowFeature::kFeatureTabStrip)) {
    return;
  }

  TabStripModel* model = nav_params->browser->GetTabStripModel();
  DCHECK(model);

  const int source_index =
      model->GetIndexOfWebContents(nav_params->source_contents);

  // If the source tab is not in the current tab strip (e.g. if the current
  // navigation is in a new window), don't set the group. Groups cannot be
  // shared across multiple windows.
  if (source_index == TabStripModel::kNoTab) {
    return;
  }

  // Do not set the group when the navigation is from bookmarks.
  if (ui::PageTransitionCoreTypeIs(nav_params->transition,
                                   ui::PAGE_TRANSITION_AUTO_BOOKMARK)) {
    return;
  }

  if (nav_params->disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
      nav_params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
    nav_params->group = model->GetTabGroupForTab(source_index);
  }
}

}  // namespace chrome
