// Copyright 2020 The Chromium Authors. All rights reserved.
// 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 "base/callback_helpers.h"
#include "base/fuchsia/fuchsia_logging.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(fuchsia::logger::LogMessage message,
                              LogCallback callback) {
  if (on_log_message_)
    on_log_message_.Run(message);
  callback();
}

void TestLogListenerSafe::LogMany(
    std::vector<fuchsia::logger::LogMessage> messages,
    LogManyCallback callback) {
  for (const auto& message : messages)
    on_log_message_.Run(message);
  callback();
}

void TestLogListenerSafe::Done() {}

void TestLogListenerSafe::NotImplemented_(const std::string& name) {
  ADD_FAILURE() << "NotImplemented_: " << name;
}

SimpleTestLogListener::SimpleTestLogListener() : binding_(&listener_) {}

SimpleTestLogListener::~SimpleTestLogListener() = default;

void SimpleTestLogListener::ListenToLog(
    fuchsia::logger::Log* log,
    std::unique_ptr<fuchsia::logger::LogFilterOptions> options) {
  ignore_before_ = zx::clock::get_monotonic();
  listener_.set_on_log_message(base::BindRepeating(
      &SimpleTestLogListener::PushLoggedMessage, base::Unretained(this)));
  log->ListenSafe(binding_.NewBinding(), std::move(options));
}

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;
  binding_.set_error_handler(
      [quit_loop = loop.QuitClosure()](zx_status_t status) {
        ZX_LOG(ERROR, status) << "LogListenerSafe disconnected";
        quit_loop.Run();
      });
  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();

  binding_.set_error_handler({});
  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));
  }
}

}  // namespace base
