// 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/contextual_tasks/contextual_tasks_panel_host_desktop.h"

#include "chrome/browser/contextual_tasks/active_task_context_provider.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_panel_host.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_web_view.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/side_panel/side_panel_entry.h"
#include "chrome/browser/ui/side_panel/side_panel_enums.h"
#include "chrome/browser/ui/side_panel/side_panel_registry.h"
#include "chrome/browser/ui/side_panel/side_panel_ui.h"
#include "chrome/browser/ui/views/interaction/browser_elements_views.h"
#include "components/contextual_tasks/public/features.h"
#include "content/public/browser/web_contents.h"
#include "ui/compositor/layer.h"

namespace {
// Configuration for the desired width of the side panel.
inline constexpr int kSidePanelPreferredDefaultWidth = 440;
}  // namespace

namespace contextual_tasks {

ContextualTasksPanelHostDesktop::ContextualTasksPanelHostDesktop(
    BrowserWindowInterface* browser_window)
    : browser_window_(browser_window) {
  CreateAndRegisterEntry();
}

ContextualTasksPanelHostDesktop::~ContextualTasksPanelHostDesktop() {
  SidePanelRegistry* global_registry = SidePanelRegistry::From(browser_window_);
  if (global_registry) {
    auto* contextual_tasks_entry = global_registry->GetEntryForKey(
        SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks));
    if (contextual_tasks_entry) {
      contextual_tasks_entry->RemoveObserver(this);
    }
  }
}

void ContextualTasksPanelHostDesktop::AddObserver(
    ContextualTasksPanelHost::Observer* observer) {
  observers_.AddObserver(observer);
}

void ContextualTasksPanelHostDesktop::RemoveObserver(
    ContextualTasksPanelHost::Observer* observer) {
  observers_.RemoveObserver(observer);
}

void ContextualTasksPanelHostDesktop::Show(
    ContextualTasksPanelHost::AnimationStyle animation) {
  // Only show the side panel if it's closed.
  auto* side_panel_ui = SidePanelUI::From(browser_window_);
  if (!side_panel_ui || IsPanelOpenForContextualTask()) {
    return;
  }

  switch (animation) {
    case ContextualTasksPanelHost::AnimationStyle::kStandard:
      side_panel_ui->Show(
          SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks));
      break;
    case ContextualTasksPanelHost::AnimationStyle::kTransitionFromTab:
      ShowFromTab();
      break;
    case ContextualTasksPanelHost::AnimationStyle::kNoAnimation:
      side_panel_ui->Show(
          SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks),
          /*open_trigger=*/std::nullopt,
          /*suppress_animations=*/true);
      break;
  }
}

void ContextualTasksPanelHostDesktop::Close(
    ContextualTasksPanelHost::AnimationStyle animation) {
  auto* side_panel_ui = SidePanelUI::From(browser_window_);
  if (!side_panel_ui) {
    return;
  }

  // `kTransitionFromTab` is only supported for showing the panel.
  CHECK_NE(animation,
           ContextualTasksPanelHost::AnimationStyle::kTransitionFromTab);

  side_panel_ui->Close(
      SidePanelEntryHideReason::kSidePanelClosed,
      /*suppress_animations=*/animation ==
          ContextualTasksPanelHost::AnimationStyle::kNoAnimation);
}

bool ContextualTasksPanelHostDesktop::IsPanelInitialized() {
  return web_view_ != nullptr;
}

bool ContextualTasksPanelHostDesktop::IsPanelOpenForContextualTask() const {
  auto* side_panel_ui = SidePanelUI::From(browser_window_);
  return side_panel_ui &&
         side_panel_ui->IsSidePanelEntryShowing(
             SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks));
}

bool ContextualTasksPanelHostDesktop::IsPanelSuppressed() const {
  auto* side_panel_ui = SidePanelUI::From(browser_window_);
  if (!side_panel_ui || suppressed_for_testing_) {
    return true;
  }
  // If the glic side panel is open, do not override it with the contextual
  // tasks side panel when active tab is changed.
  return side_panel_ui->IsSidePanelEntryShowing(
      SidePanelEntry::Key(SidePanelEntry::Id::kGlic));
}

void ContextualTasksPanelHostDesktop::SetPanelSuppressedForTesting(  // IN-TEST
    bool suppressed) {
  suppressed_for_testing_ = suppressed;
}

content::WebContents* ContextualTasksPanelHostDesktop::GetWebContents() {
  if (!IsPanelInitialized()) {
    return nullptr;
  }
  return web_view_->web_contents();
}

content::WebContents* ContextualTasksPanelHostDesktop::GetToolbarWebContents() {
  if (IsContextualTasksSidePanelRearchitectureEnabled()) {
    return web_view_ && web_view_->toolbar_web_view()
               ? web_view_->toolbar_web_view()->GetWebContents()
               : nullptr;
  }
  return GetWebContents();
}

void ContextualTasksPanelHostDesktop::SetWebContents(
    content::WebContents* web_contents) {
  if (IsPanelInitialized()) {
    web_view_->SetWebContents(web_contents);
  }
}

void ContextualTasksPanelHostDesktop::OnEntryShown(SidePanelEntry* entry) {
  NotifySurfaceStateChanged(
      ContextualTasksPanelHost::SurfaceState::kVisible,
      ContextualTasksPanelHost::StateChangeReason::kUserAction);
}

void ContextualTasksPanelHostDesktop::OnEntryHidden(SidePanelEntry* entry) {
  NotifySurfaceStateChanged(
      ContextualTasksPanelHost::SurfaceState::kClosed,
      ContextualTasksPanelHost::StateChangeReason::kUserAction);
}

std::unique_ptr<views::View>
ContextualTasksPanelHostDesktop::CreateSidePanelView(
    SidePanelEntryScope& scope) {
  std::unique_ptr<ContextualTasksWebView> web_view =
      std::make_unique<ContextualTasksWebView>(browser_window_);
  web_view->SetPaintToLayer();
  web_view->layer()->SetFillsBoundsOpaquely(false);
  web_view_ = web_view->GetWeakPtr();

  NotifySurfaceStateChanged(
      ContextualTasksPanelHost::SurfaceState::kVisible,
      ContextualTasksPanelHost::StateChangeReason::kSystemAction);

  return web_view;
}

void ContextualTasksPanelHostDesktop::NotifySurfaceStateChanged(
    ContextualTasksPanelHost::SurfaceState state,
    ContextualTasksPanelHost::StateChangeReason reason) {
  observers_.Notify(&ContextualTasksPanelHost::Observer::OnSurfaceStateChanged,
                    state, reason);
}

void ContextualTasksPanelHostDesktop::CreateAndRegisterEntry() {
  auto* global_registry = SidePanelRegistry::From(browser_window_);
  CHECK(global_registry);

  if (global_registry->GetEntryForKey(
          SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks))) {
    return;
  }

  auto entry = std::make_unique<SidePanelEntry>(
      SidePanelType::kToolbar,
      SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks),
      base::BindRepeating(
          [](base::WeakPtr<ContextualTasksPanelHostDesktop> host,
             SidePanelEntryScope& scope) -> std::unique_ptr<views::View> {
            return host ? host->CreateSidePanelView(scope) : nullptr;
          },
          weak_factory_.GetWeakPtr()),
      base::BindRepeating([]() { return kSidePanelPreferredDefaultWidth; }));
  entry->set_should_show_ephemerally_in_toolbar(false);
  entry->set_should_show_header(false);
  global_registry->Register(std::move(entry));

  // Observe the side panel entry.
  auto* registered_entry = global_registry->GetEntryForKey(
      SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks));
  registered_entry->AddObserver(this);
}

void ContextualTasksPanelHostDesktop::ShowFromTab() {
  auto* side_panel_ui = SidePanelUI::From(browser_window_);
  if (!side_panel_ui) {
    return;
  }

  views::View* content = BrowserElementsViews::From(browser_window_)
                             ->RetrieveView(kActiveContentsWebViewRetrievalId);
  gfx::Rect content_bounds_in_browser_coordinates =
      content->ConvertRectToWidget(content->GetContentsBounds());
  side_panel_ui->ShowFrom(
      SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks),
      content_bounds_in_browser_coordinates);
}

}  // namespace contextual_tasks
