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

#include "base/fuchsia/test_log_listener_safe.h"

#include <lib/async/default.h>
#include <lib/fidl/cpp/box.h>
#include <lib/zx/clock.h>

#include "base/fuchsia/fuchsia_component_connect.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/functional/callback_helpers.h"
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "base/test/bind.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace base {

TestLogListenerSafe::TestLogListenerSafe() = default;

TestLogListenerSafe::~TestLogListenerSafe() = default;

void TestLogListenerSafe::set_on_log_message(
    base::RepeatingCallback<void(const fuchsia_logger::LogMessage&)> callback) {
  on_log_message_ = std::move(callback);
}

void TestLogListenerSafe::Log(
    TestLogListenerSafe::LogRequest& request,
    TestLogListenerSafe::LogCompleter::Sync& completer) {
  if (on_log_message_)
    on_log_message_.Run(request.log());
  completer.Reply();
}

void TestLogListenerSafe::LogMany(
    TestLogListenerSafe::LogManyRequest& request,
    TestLogListenerSafe::LogManyCompleter::Sync& completer) {
  for (const auto& message : request.log()) {
    on_log_message_.Run(message);
  }
  completer.Reply();
}

void TestLogListenerSafe::Done(
    TestLogListenerSafe::DoneCompleter::Sync& completer) {}

SimpleTestLogListener::SimpleTestLogListener() = default;
SimpleTestLogListener::~SimpleTestLogListener() = default;

void SimpleTestLogListener::ListenToLog(
    const fidl::Client<fuchsia_logger::Log>& log,
    std::unique_ptr<fuchsia_logger::LogFilterOptions> options) {
  auto listener_endpoints =
      fidl::CreateEndpoints<fuchsia_logger::LogListenerSafe>();
  ZX_CHECK(listener_endpoints.is_ok(), listener_endpoints.status_value())
      << "Failed to create listener endpoints";
  binding_.emplace(
      async_get_default_dispatcher(), std::move(listener_endpoints->server),
      &listener_, [](fidl::UnbindInfo info) {
        ZX_LOG(ERROR, info.status()) << "LogListenerSafe disconnected";
      });

  ignore_before_ = zx::clock::get_monotonic();
  listener_.set_on_log_message(base::BindRepeating(
      &SimpleTestLogListener::PushLoggedMessage, base::Unretained(this)));
  auto listen_safe_result =
      log->ListenSafe({{.log_listener = std::move(listener_endpoints->client),
                        .options = std::move(options)}});
  if (listen_safe_result.is_error()) {
    ZX_DLOG(ERROR, listen_safe_result.error_value().status())
        << "ListenSafe() failed";
  }
}

absl::optional<fuchsia_logger::LogMessage>
SimpleTestLogListener::RunUntilMessageReceived(
    base::StringPiece expected_string) {
  while (!logged_messages_.empty()) {
    fuchsia_logger::LogMessage message = logged_messages_.front();
    logged_messages_.pop_front();
    if (base::StringPiece(message.msg()).find(expected_string) !=
        std::string::npos) {
      return message;
    }
  }

  absl::optional<fuchsia_logger::LogMessage> logged_message;
  base::RunLoop loop;
  on_log_message_ = base::BindLambdaForTesting(
      [ignore_before = ignore_before_, &logged_message,
       expected_string = std::string(expected_string),
       quit_loop =
           loop.QuitClosure()](const fuchsia_logger::LogMessage& message) {
        if (zx::time(message.time()) < ignore_before) {
          return;
        }
        if (message.msg().find(expected_string) == std::string::npos) {
          return;
        }
        logged_message.emplace(message);
        quit_loop.Run();
      });

  loop.Run();

  on_log_message_ = NullCallback();

  return logged_message;
}

void SimpleTestLogListener::PushLoggedMessage(
    const fuchsia_logger::LogMessage& message) {
  DVLOG(1) << "TestLogListener received: " << message.msg();
  if (zx::time(message.time()) < ignore_before_) {
    return;
  }
  if (on_log_message_) {
    DCHECK(logged_messages_.empty());
    on_log_message_.Run(message);
  } else {
    logged_messages_.push_back(std::move(message));
  }
}

void ListenFilteredByCurrentProcessId(SimpleTestLogListener& listener) {
  // Connect the test LogListenerSafe to the Log.
  auto log_client_end = fuchsia_component::Connect<fuchsia_logger::Log>();
  ASSERT_TRUE(log_client_end.is_ok())
      << FidlConnectionErrorMessage(log_client_end);
  fidl::Client log_client(std::move(log_client_end.value()),
                          async_get_default_dispatcher());
  listener.ListenToLog(
      log_client,
      std::make_unique<fuchsia_logger::LogFilterOptions>(
          fuchsia_logger::LogFilterOptions{
              {.filter_by_pid = true,
               .pid = Process::Current().Pid(),
               .min_severity = fuchsia_logger::LogLevelFilter::kInfo}}));
}

}  // namespace base
