// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "weblayer/browser/webui/net_export_ui.h"

#include "base/command_line.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/net_log/net_export_file_writer.h"
#include "components/net_log/net_export_ui_constants.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.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/browser/web_ui_message_handler.h"
#include "weblayer/browser/system_network_context_manager.h"
#include "weblayer/grit/weblayer_resources.h"

#if BUILDFLAG(IS_ANDROID)
#include "components/browser_ui/share/android/intent_helper.h"
#endif

namespace weblayer {

namespace {

class NetExportMessageHandler
    : public content::WebUIMessageHandler,
      public net_log::NetExportFileWriter::StateObserver {
 public:
  NetExportMessageHandler()
      : file_writer_(SystemNetworkContextManager::GetInstance()
                         ->GetNetExportFileWriter()) {
    file_writer_->Initialize();
  }

  NetExportMessageHandler(const NetExportMessageHandler&) = delete;
  NetExportMessageHandler& operator=(const NetExportMessageHandler&) = delete;

  ~NetExportMessageHandler() override { file_writer_->StopNetLog(); }

  // content::WebUIMessageHandler implementation.
  void RegisterMessages() override {
    web_ui()->RegisterMessageCallback(
        net_log::kEnableNotifyUIWithStateHandler,
        base::BindRepeating(&NetExportMessageHandler::OnEnableNotifyUIWithState,
                            base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        net_log::kStartNetLogHandler,
        base::BindRepeating(&NetExportMessageHandler::OnStartNetLog,
                            base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        net_log::kStopNetLogHandler,
        base::BindRepeating(&NetExportMessageHandler::OnStopNetLog,
                            base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        net_log::kSendNetLogHandler,
        base::BindRepeating(&NetExportMessageHandler::OnSendNetLog,
                            base::Unretained(this)));
  }

  // Messages
  void OnEnableNotifyUIWithState(const base::Value::List& list) {
    AllowJavascript();
    if (!state_observation_manager_.IsObserving()) {
      state_observation_manager_.Observe(file_writer_.get());
    }
    NotifyUIWithState(file_writer_->GetState());
  }

  void OnStartNetLog(const base::Value::List& params) {
    // Determine the capture mode.
    if (!params.empty() && params[0].is_string()) {
      capture_mode_ = net_log::NetExportFileWriter::CaptureModeFromString(
          params[0].GetString());
    }

    // Determine the max file size.
    if (params.size() > 1 && params[1].is_int() && params[1].GetInt() > 0)
      max_log_file_size_ = params[1].GetInt();

    StartNetLog(base::FilePath());
  }

  void OnStopNetLog(const base::Value::List& list) {
    file_writer_->StopNetLog();
  }

  void OnSendNetLog(const base::Value::List& list) {
    file_writer_->GetFilePathToCompletedLog(
        base::BindOnce(&NetExportMessageHandler::SendEmail));
  }

  // net_log::NetExportFileWriter::StateObserver implementation.
  void OnNewState(const base::Value::Dict& state) override {
    NotifyUIWithState(state);
  }

 private:
  // Send NetLog data via email.
  static void SendEmail(const base::FilePath& file_to_send) {
#if BUILDFLAG(IS_ANDROID)
    if (file_to_send.empty())
      return;
    std::string email;
    std::string subject = "WebLayer net_internals_log";
    std::string title = "Issue number: ";
    std::string body =
        "Please add some informative text about the network issues.";
    base::FilePath::StringType file_to_attach(file_to_send.value());
    browser_ui::SendEmail(base::ASCIIToUTF16(email),
                          base::ASCIIToUTF16(subject), base::ASCIIToUTF16(body),
                          base::ASCIIToUTF16(title),
                          base::ASCIIToUTF16(file_to_attach));
#endif
  }

  void StartNetLog(const base::FilePath& path) {
    file_writer_->StartNetLog(
        path, capture_mode_, max_log_file_size_,
        base::CommandLine::ForCurrentProcess()->GetCommandLineString(),
        std::string(),
        web_ui()
            ->GetWebContents()
            ->GetBrowserContext()
            ->GetDefaultStoragePartition()
            ->GetNetworkContext());
  }

  // Fires net-log-info-changed event to update the JavaScript UI in the
  // renderer.
  void NotifyUIWithState(const base::Value::Dict& state) {
    FireWebUIListener(net_log::kNetLogInfoChangedEvent, state);
  }

  // Cached pointer to SystemNetworkContextManager's NetExportFileWriter.
  raw_ptr<net_log::NetExportFileWriter> file_writer_;

  base::ScopedObservation<net_log::NetExportFileWriter,
                          net_log::NetExportFileWriter::StateObserver>
      state_observation_manager_{this};

  // The capture mode and file size bound that the user chose in the UI when
  // logging started is cached here and is read after a file path is chosen in
  // the save dialog. Their values are only valid while the save dialog is open
  // on the desktop UI.
  net::NetLogCaptureMode capture_mode_ = net::NetLogCaptureMode::kDefault;
  uint64_t max_log_file_size_ = net_log::NetExportFileWriter::kNoLimit;
};

}  // namespace

const char kChromeUINetExportHost[] = "net-export";

NetExportUI::NetExportUI(content::WebUI* web_ui) : WebUIController(web_ui) {
  web_ui->AddMessageHandler(std::make_unique<NetExportMessageHandler>());

  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      web_ui->GetWebContents()->GetBrowserContext(), kChromeUINetExportHost);
  source->UseStringsJs();
  source->AddResourcePath(net_log::kNetExportUICSS, IDR_NET_LOG_NET_EXPORT_CSS);
  source->AddResourcePath(net_log::kNetExportUIJS, IDR_NET_LOG_NET_EXPORT_JS);
  source->SetDefaultResource(IDR_NET_LOG_NET_EXPORT_HTML);
}

NetExportUI::~NetExportUI() = default;

WEB_UI_CONTROLLER_TYPE_IMPL(NetExportUI)

}  // namespace weblayer