// 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.

#include "chrome/browser/ash/app_list/search/omnibox/open_tab_result.h"

#include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
#include "ash/public/cpp/style/dark_light_mode_controller.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/app_list/app_list_controller_delegate.h"
#include "chrome/browser/ash/app_list/search/common/icon_constants.h"
#include "chrome/browser/ash/app_list/search/common/search_result_util.h"
#include "chrome/browser/ash/app_list/search/common/string_util.h"
#include "chrome/browser/ash/app_list/search/omnibox/omnibox_types.h"
#include "chrome/browser/ash/app_list/search/omnibox/omnibox_util.h"
#include "chromeos/ash/components/string_matching/tokenized_string.h"
#include "chromeos/ash/components/string_matching/tokenized_string_match.h"
#include "components/omnibox/browser/favicon_cache.h"
#include "components/omnibox/browser/vector_icons.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/window_open_disposition.h"
#include "ui/base/window_open_disposition_utils.h"
#include "ui/gfx/paint_vector_icon.h"
#include "url/gurl.h"

namespace app_list {

namespace {

using ::ash::string_matching::TokenizedString;
using ::ash::string_matching::TokenizedStringMatch;

constexpr char kOpenTabScheme[] = "opentab://";

constexpr char16_t kUrlDelimiter[] = u" - ";

constexpr char16_t kA11yDelimiter[] = u", ";

}  // namespace

OpenTabResult::OpenTabResult(Profile* profile,
                             AppListControllerDelegate* list_controller,
                             std::unique_ptr<OmniboxResultData> search_result,
                             const TokenizedString& query,
                             FaviconCache* favicon_cache)
    : profile_(profile),
      list_controller_(list_controller),
      search_result_(std::move(search_result)),
      drive_id_(GetDriveId(search_result_->destination_url)),
      description_(search_result_->description.value_or(u"")) {
  CHECK(favicon_cache);
  DCHECK(search_result_->destination_url.is_valid());

  // TODO(crbug.com/1293702): This may not be unique. Once we have a mechanism
  // for opening a specific tab, add that info too to ensure uniqueness.
  set_id(kOpenTabScheme + search_result_->destination_url.spec());

  SetDisplayType(DisplayType::kList);
  SetResultType(ResultType::kOpenTab);
  SetMetricsType(ash::OPEN_TAB);
  SetCategory(Category::kWeb);

  // Ignore `search_result_->relevance` and manually calculate a relevance
  // score for this result.
  TokenizedStringMatch string_match;
  TokenizedString title(description_);
  set_relevance(string_match.Calculate(query, title));

  UpdateText();
  FetchFavicon(favicon_cache);
  UpdateIcon();

  if (auto* dark_light_mode_controller = ash::DarkLightModeController::Get())
    dark_light_mode_controller->AddObserver(this);
}

OpenTabResult::~OpenTabResult() {
  if (auto* dark_light_mode_controller = ash::DarkLightModeController::Get())
    dark_light_mode_controller->RemoveObserver(this);
}

void OpenTabResult::Open(int event_flags) {
  list_controller_->OpenURL(
      profile_, search_result_->destination_url,
      search_result_->page_transition,
      ui::DispositionFromEventFlags(event_flags,
                                    WindowOpenDisposition::SWITCH_TO_TAB));
}

std::optional<GURL> OpenTabResult::url() const {
  return search_result_->destination_url;
}

std::optional<std::string> OpenTabResult::DriveId() const {
  return drive_id_;
}

void OpenTabResult::OnColorModeChanged(bool dark_mode_enabled) {
  if (uses_generic_icon_)
    SetGenericIcon();
}

void OpenTabResult::FetchFavicon(FaviconCache* favicon_cache) {
  CHECK(favicon_cache);
  CHECK(search_result_->favicon.isNull());
  CHECK(IsEligibleForFavicon(search_result_->omnibox_type));
  gfx::Image favicon = favicon_cache->GetFaviconForPageUrl(
      search_result_->destination_url,
      base::BindOnce(&OpenTabResult::OnFetchedFavicon,
                     weak_factory_.GetWeakPtr()));
  if (!favicon.IsEmpty()) {
    OnFetchedFavicon(favicon);
  }
}

void OpenTabResult::OnFetchedFavicon(const gfx::Image& icon) {
  CHECK(search_result_->favicon.isNull());
  auto image_skia = icon.AsImageSkia();
  CHECK(!image_skia.isNull());
  search_result_->favicon = image_skia;
  CHECK(!search_result_->favicon.isNull());
  SetIcon(
      IconInfo(ui::ImageModel::FromImageSkia(image_skia), kFaviconDimension));
}

void OpenTabResult::UpdateText() {
  // URL results from the Omnibox have the page title stored in the description.
  SetTitle(description_);

  const std::u16string url =
      base::UTF8ToUTF16(search_result_->destination_url.spec());
  SetDetailsTextVector(
      {CreateStringTextItem(url).SetTextTags({Tag(Tag::URL, 0, url.length())}),
       CreateStringTextItem(l10n_util::GetStringFUTF16(
                                IDS_APP_LIST_OPEN_TAB_HINT, kUrlDelimiter))
           .SetOverflowBehavior(
               ash::SearchResultTextItem::OverflowBehavior::kNoElide)});

  SetAccessibleName(base::JoinString(
      {description_, url,
       l10n_util::GetStringFUTF16(IDS_APP_LIST_OPEN_TAB_HINT, u"")},
      kA11yDelimiter));
}

void OpenTabResult::UpdateIcon() {
  // Use a favicon if one is available.
  if (!search_result_->favicon.isNull()) {
    SetIcon(IconInfo(ui::ImageModel::FromImageSkia(search_result_->favicon),
                     kFaviconDimension));
    return;
  }

  // Otherwise, fall back to using a generic icon.
  // TODO(crbug.com/1293702): WIP. Decide on the right generic icon here.
  SetGenericIcon();
}

void OpenTabResult::SetGenericIcon() {
  uses_generic_icon_ = true;
  SetIcon(IconInfo(
      ui::ImageModel::FromVectorIcon(features::IsRoundedIconsEnabled()
                                         ? omnibox::kTabIcon
                                         : omnibox::kSwitchOldIcon,
                                     kGenericIconColorId, kSystemIconDimension),
      kSystemIconDimension));
}

}  // namespace app_list
