// Copyright (c) 2023 Salesforce, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/common/gin_helper/reply_channel.h"

#include "base/debug/stack_trace.h"
#include "gin/data_object_builder.h"
#include "gin/object_template_builder.h"
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_converters/blink_converter.h"
#include "shell/common/gin_helper/handle.h"

namespace gin_helper::internal {

// static
using InvokeCallback = electron::mojom::ElectronApiIPC::InvokeCallback;
gin_helper::Handle<ReplyChannel> ReplyChannel::Create(v8::Isolate* isolate,
                                                      InvokeCallback callback) {
  return gin_helper::CreateHandle(isolate,
                                  new ReplyChannel(std::move(callback)));
}

gin::ObjectTemplateBuilder ReplyChannel::GetObjectTemplateBuilder(
    v8::Isolate* isolate) {
  return gin_helper::DeprecatedWrappable<
             ReplyChannel>::GetObjectTemplateBuilder(isolate)
      .SetMethod("sendReply", &ReplyChannel::SendReply);
}

const char* ReplyChannel::GetTypeName() {
  return "ReplyChannel";
}

ReplyChannel::ReplyChannel(InvokeCallback callback)
    : callback_(std::move(callback)) {}

ReplyChannel::~ReplyChannel() {
  if (callback_)
    SendError("reply was never sent");
}

void ReplyChannel::SendError(const std::string& msg) {
  v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
  // If there's no current context, it means we're shutting down, so we
  // don't need to send an event.
  v8::HandleScope scope(isolate);
  if (!isolate->GetCurrentContext().IsEmpty()) {
    auto message = gin::DataObjectBuilder(isolate).Set("error", msg).Build();
    SendReply(isolate, message);
  }
}

bool ReplyChannel::SendReply(v8::Isolate* isolate, v8::Local<v8::Value> arg) {
  if (!callback_)
    return false;
  blink::CloneableMessage message;
  if (!gin::ConvertFromV8(isolate, arg, &message)) {
    return false;
  }

  std::move(callback_).Run(std::move(message));
  return true;
}

gin::DeprecatedWrapperInfo ReplyChannel::kWrapperInfo = {
    gin::kEmbedderNativeGin};

}  // namespace gin_helper::internal
