// Copyright 2023 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/webui/ash/mako/mako_ui.h"

#include <algorithm>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/lobster/lobster_controller.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "chrome/browser/ash/input_method/editor_mediator_factory.h"
#include "chrome/browser/ash/lobster/lobster_service.h"
#include "chrome/browser/ash/lobster/lobster_service_provider.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/mako/url_constants.h"
#include "chrome/browser/ui/webui/top_chrome/untrusted_top_chrome_web_ui_controller.h"
#include "chrome/grit/orca_resources.h"
#include "chrome/grit/orca_resources_map.h"
#include "chromeos/ash/services/orca/public/mojom/orca_service.mojom.h"
#include "chromeos/constants/chromeos_features.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/url_constants.h"
#include "ui/webui/webui_util.h"

namespace ash {
namespace {

constexpr int kLobsterResourceIds[] = {
    IDR_MAKO_LOBSTER_HTML,
    IDR_MAKO_LOBSTER_JS,
};

}  // namespace

MakoUntrustedUIConfig::MakoUntrustedUIConfig()
    : DefaultTopChromeWebUIConfig(content::kChromeUIUntrustedScheme,
                                  ash::kChromeUIMakoHost) {}

MakoUntrustedUIConfig::~MakoUntrustedUIConfig() = default;

bool MakoUntrustedUIConfig::IsWebUIEnabled(
    content::BrowserContext* browser_context) {
  return chromeos::features::IsOrcaEnabled() ||
         ash::features::IsLobsterEnabled();
}

bool MakoUntrustedUIConfig::ShouldAutoResizeHost() {
  // With resizing support enabled, we should let web viewport resize according
  // to dimension of web view rather than updating the dimension of web view
  // based on inner web content.
  return !base::FeatureList::IsEnabled(ash::features::kOrcaResizingSupport);
}

MakoUntrustedUI::MakoUntrustedUI(content::WebUI* web_ui)
    : UntrustedTopChromeWebUIController(web_ui) {
  CHECK(chromeos::features::IsOrcaEnabled() ||
        ash::features::IsLobsterEnabled());

  // Setup the data source
  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      web_ui->GetWebContents()->GetBrowserContext(), kChromeUIMakoURL);

  LobsterService* lobster_service =
      ash::features::IsLobsterEnabled()
          ? LobsterServiceProvider::GetForProfile(Profile::FromWebUI(web_ui))
          : nullptr;
  auto should_use_resource =
      [&](const webui::ResourcePath& resource_path) -> bool {
    // when lobster access is not granted, lobster resources are not allowed.
    if (lobster_service == nullptr &&
        std::ranges::contains(kLobsterResourceIds, resource_path.id)) {
      return false;
    }
    return true;
  };

  // TODO: b:333625296 - Add tests for this conditional behavior
  {
    std::vector<webui::ResourcePath> allowed_resources;
    std::ranges::copy_if(kOrcaResources, std::back_inserter(allowed_resources),
                         should_use_resource);
    webui::SetupWebUIDataSource(source, allowed_resources, IDR_MAKO_ORCA_HTML);
  }

  source->SetDefaultResource(IDR_MAKO_ORCA_HTML);

  // Setup additional CSP overrides
  // Intentional space at end of the strings - things are appended to this.
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::TrustedTypes,
      "trusted-types goog#html polymer_resin lit-html "
      "polymer-template-event-attribute-policy polymer-html-literal; ");
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::StyleSrc,
      "style-src 'unsafe-inline'  chrome-untrusted://theme; ");
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ImgSrc, "img-src data:; ");
}
MakoUntrustedUI::~MakoUntrustedUI() = default;

void MakoUntrustedUI::BindInterface(
    mojo::PendingReceiver<orca::mojom::EditorClient> pending_receiver) {
  if (!chromeos::features::IsOrcaEnabled()) {
    mojo::ReportBadMessage("Editor is disabled by flags.");
    return;
  }

  // If mako ui is shown to the user, then we know that EditorMediator is
  // allowed for the current profile and will return a valid instance.
  input_method::EditorMediatorFactory::GetInstance()
      ->GetForProfile(Profile::FromWebUI(web_ui()))
      ->BindEditorClient(std::move(pending_receiver));
}

void MakoUntrustedUI::BindInterface(
    mojo::PendingReceiver<lobster::mojom::UntrustedLobsterPageHandler>
        pending_receiver) {
  if (!ash::features::IsLobsterEnabled()) {
    mojo::ReportBadMessage("Lobster is disabled by flags.");
    return;
  }

  Profile* profile = Profile::FromWebUI(web_ui());
  LobsterService* lobster_service =
      LobsterServiceProvider::GetForProfile(profile);
  LobsterSession* active_session =
      lobster_service == nullptr ? nullptr : lobster_service->active_session();

  if (active_session == nullptr) {
    mojo::ReportBadMessage("No active session found.");
    return;
  }

  lobster_page_handler_ =
      std::make_unique<LobsterPageHandler>(active_session, profile);

  lobster_page_handler_->BindInterface(std::move(pending_receiver));
}

WEB_UI_CONTROLLER_TYPE_IMPL(MakoUntrustedUI)

}  // namespace ash
