// Copyright 2026 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/actor/actor_util.h"

#include <optional>
#include <vector>

#include "chrome/browser/actor/actor_keyed_service.h"
#include "chrome/browser/actor/execution_engine.h"
#include "chrome/browser/glic/public/glic_instance.h"
#include "chrome/browser/glic/public/glic_keyed_service.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/web_contents.h"
#include "skia/ext/font_utils.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkFont.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"

namespace actor {

bool HaveActiveTaskForContents(content::WebContents* source_contents) {
  if (!source_contents) {
    return false;
  }

  actor::ActorKeyedService* actor_service =
      actor::ActorKeyedService::Get(source_contents->GetBrowserContext());
  if (!actor_service) {
    return false;
  }

  return actor_service->GetActingActorTaskForWebContents(source_contents);
}

bool IsRunningBackgroundActorTask(content::WebContents& source_contents) {
  if (!HaveActiveTaskForContents(&source_contents)) {
    return false;
  }

  tabs::TabInterface* task_tab =
      tabs::TabInterface::GetFromContents(&source_contents);
  if (task_tab->IsActivated()) {
    // The tab in which the task is running is active, so the task isn't in the
    // background.
    return false;
  }

  // Determine whether the active tab is showing the conversation instance of
  // the actor task. If the conversation is showing, consider the task to be in
  // the foreground.

  glic::GlicKeyedService* glic_service =
      glic::GlicKeyedService::Get(source_contents.GetBrowserContext());
  if (!glic_service) {
    // Without Glic, there'd be no conversation to provide context to the user
    // while the tab isn't active, so consider the task backgrounded.
    return true;
  }

  const glic::GlicInstance* task_instance =
      glic_service->GetInstanceForTab(task_tab);
  if (!task_instance) {
    // There's no Glic instance for the tab running the task (e.g. something
    // else is invoking the actor framework). As above, there's no conversation,
    // so consider the task backgrounded.
    return true;
  }
  BrowserWindowInterface* task_window = task_tab->GetBrowserWindowInterface();
  const glic::GlicInstance* active_instance =
      glic_service->GetInstanceForActiveTab(task_window);
  if (task_instance != active_instance) {
    // The active tab has no Glic instance or an unrelated instance. The user
    // doesn't see the conversation related to the task, so consider the task
    // backgrounded.
    return true;
  }

  return !task_instance->IsShowing();
}

bool HasActorTaskPreventingNewWebContents(content::RenderFrameHost* rfh) {
  auto* wc = content::WebContents::FromRenderFrameHost(rfh);
  if (!wc) {
    return false;
  }

  auto* actor_service = ActorKeyedService::Get(wc->GetBrowserContext());
  if (!actor_service) {
    return false;
  }

  const auto* tab_interface = tabs::TabInterface::MaybeGetFromContents(wc);
  if (!tab_interface) {
    return false;
  }

  const ActorTask* task = actor_service->GetTaskFromTab(*tab_interface);
  if (!task) {
    return false;
  }

  return !task->GetExecutionEngine().TabsCanOpenNewWebContents();
}

std::optional<std::vector<uint8_t>> GetScreenshotWithIframeBoundingBoxes(
    const std::vector<uint8_t>& screenshot_data,
    std::string_view mime_type,
    const optimization_guide::proto::ScreenshotInfo& screenshot_info) {
  if (screenshot_info.iframe_info_size() == 0) {
    return screenshot_data;
  }

  SkBitmap bitmap;
  if (mime_type == "image/png") {
    bitmap = gfx::PNGCodec::Decode(screenshot_data);
  } else {
    bitmap = gfx::JPEGCodec::Decode(screenshot_data);
  }

  if (bitmap.isNull()) {
    return std::nullopt;
  }

  SkCanvas canvas(bitmap);
  SkPaint paint;
  paint.setColor(SK_ColorRED);
  paint.setStyle(SkPaint::kStroke_Style);
  paint.setStrokeWidth(3.0f);

  for (const auto& iframe : screenshot_info.iframe_info()) {
    if (iframe.has_bounding_box()) {
      const auto& rect = iframe.bounding_box();
      canvas.drawRect(
          SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()),
          paint);

      if (iframe.has_security_origin() &&
          !iframe.security_origin().value().empty()) {
        SkFont font = skia::DefaultFont();
        font.setSize(12.0f);
        std::string origin_str = iframe.security_origin().value();
        SkRect text_bounds;
        font.measureText(origin_str.c_str(), origin_str.size(),
                         SkTextEncoding::kUTF8, &text_bounds);

        SkRect bg_rect = SkRect::MakeXYWH(
            rect.x() + 5.0f + text_bounds.left() - 2.0f,
            rect.y() + 15.0f + text_bounds.top() - 2.0f,
            text_bounds.width() + 4.0f, text_bounds.height() + 4.0f);

        SkPaint bg_paint;
        bg_paint.setColor(SK_ColorWHITE);
        bg_paint.setStyle(SkPaint::kFill_Style);
        canvas.drawRect(bg_rect, bg_paint);

        SkPaint text_paint;
        text_paint.setColor(SK_ColorRED);
        canvas.drawString(origin_str.c_str(), rect.x() + 5.0f, rect.y() + 15.0f,
                          font, text_paint);
      }
    }
  }

  std::optional<std::vector<uint8_t>> encoded;
  if (mime_type == "image/png") {
    encoded = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap,
                                                /*discard_transparency=*/false);
  } else {
    encoded = gfx::JPEGCodec::Encode(bitmap, /*quality=*/100);
  }

  if (!encoded) {
    return std::nullopt;
  }
  return *encoded;
}

}  // namespace actor
