// Copyright 2017 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/extensions/api/notifications/extension_notification_handler.h"

#include "base/check_op.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h"
#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
#include "chrome/browser/notifications/notification_common.h"
#include "chrome/browser/notifications/notifier_state_tracker.h"
#include "chrome/browser/notifications/notifier_state_tracker_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/notifications.h"
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/app_window/native_app_window.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_id.h"
#include "ui/message_center/public/cpp/notifier_id.h"
#include "url/gurl.h"

namespace extensions {

namespace {

base::Value::List CreateBaseEventArgs(
    const ExtensionId& extension_id,
    const std::string& scoped_notification_id) {
  // Unscope the notification id before returning it.
  size_t index_of_separator = extension_id.length() + 1;
  DCHECK_LT(index_of_separator, scoped_notification_id.length());
  std::string unscoped_notification_id =
      scoped_notification_id.substr(index_of_separator);

  base::Value::List args;
  args.Append(unscoped_notification_id);
  return args;
}

}  // namespace

ExtensionNotificationHandler::ExtensionNotificationHandler() = default;

ExtensionNotificationHandler::~ExtensionNotificationHandler() = default;

// static
ExtensionId ExtensionNotificationHandler::GetExtensionId(const GURL& url) {
  if (!url.is_valid() || (!url.SchemeIs(kExtensionScheme)
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
                          && !url.SchemeIs(extensions::kArkwebExtensionScheme)
#endif
                              )) {
    return "";
  }
  return ExtensionId(url.DeprecatedGetOriginAsURL().host());
}

void ExtensionNotificationHandler::OnClose(
    Profile* profile,
    const GURL& origin,
    const std::string& notification_id,
    bool by_user,
    base::OnceClosure completed_closure) {
  EventRouter::UserGestureState gesture =
      by_user ? EventRouter::UserGestureState::kEnabled
              : EventRouter::UserGestureState::kNotEnabled;
  ExtensionId extension_id(GetExtensionId(GURL(origin)));
  DCHECK(!extension_id.empty());

  base::Value::List args = CreateBaseEventArgs(extension_id, notification_id);
  args.Append(by_user);
  SendEvent(profile, extension_id, events::NOTIFICATIONS_ON_CLOSED,
            api::notifications::OnClosed::kEventName, gesture, std::move(args));

  ExtensionNotificationDisplayHelper* display_helper =
      ExtensionNotificationDisplayHelperFactory::GetForProfile(profile);
  if (display_helper)
    display_helper->EraseDataForNotificationId(notification_id);

  std::move(completed_closure).Run();
}

void ExtensionNotificationHandler::OnClick(
    Profile* profile,
    const GURL& origin,
    const std::string& notification_id,
    const std::optional<int>& action_index,
    const std::optional<std::u16string>& reply,
    base::OnceClosure completed_closure) {
  DCHECK(!reply.has_value());

  ExtensionId extension_id(GetExtensionId(GURL(origin)));
  base::Value::List args = CreateBaseEventArgs(extension_id, notification_id);
  if (action_index.has_value())
    args.Append(action_index.value());
  events::HistogramValue histogram_value =
      action_index.has_value() ? events::NOTIFICATIONS_ON_BUTTON_CLICKED
                               : events::NOTIFICATIONS_ON_CLICKED;
  const std::string& event_name =
      action_index.has_value() ? api::notifications::OnButtonClicked::kEventName
                               : api::notifications::OnClicked::kEventName;

  SendEvent(profile, extension_id, histogram_value, event_name,
            EventRouter::UserGestureState::kEnabled, std::move(args));

  std::move(completed_closure).Run();
}

void ExtensionNotificationHandler::DisableNotifications(
    Profile* profile,
    const GURL& origin,
    const std::optional<std::string>& notification_id,
    const std::optional<bool>& is_suspicious) {
  message_center::NotifierId notifier_id(
      message_center::NotifierType::APPLICATION, origin.GetHost());
  NotifierStateTrackerFactory::GetForProfile(profile)->SetNotifierEnabled(
      notifier_id, false /* enabled */);
}

// There are not settings to open, but on the chance the notification shows with
// the "Open Settings" prompt, this will no-op.
void ExtensionNotificationHandler::OpenSettings(Profile* profile,
                                                const GURL& origin) {
  return;
}

void ExtensionNotificationHandler::SendEvent(
    Profile* profile,
    const ExtensionId& extension_id,
    events::HistogramValue histogram_value,
    const std::string& event_name,
    EventRouter::UserGestureState user_gesture,
    base::Value::List args) {
  if (extension_id.empty())
    return;

  EventRouter* event_router = EventRouter::Get(profile);
  if (!event_router)
    return;

  auto event =
      std::make_unique<Event>(histogram_value, event_name, std::move(args));
  event->user_gesture = user_gesture;
  event_router->DispatchEventToExtension(extension_id, std::move(event));
}

}  // namespace extensions