/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#pragma once

#include <react/bridging/Base.h>

#include <memory>
#include <optional>

namespace facebook::react {

template <>
struct Bridging<std::nullptr_t> {
  static std::nullptr_t fromJs(jsi::Runtime& rt, const jsi::Value& value) {
    if (value.isNull() || value.isUndefined()) {
      return nullptr;
    } else {
      throw jsi::JSError(rt, "Cannot convert value to nullptr");
    }
  }

  static std::nullptr_t toJs(jsi::Runtime&, std::nullptr_t) {
    return nullptr;
  }
};

template <typename T>
struct Bridging<std::optional<T>> {
  static std::optional<T> fromJs(
      jsi::Runtime& rt,
      const jsi::Value& value,
      const std::shared_ptr<CallInvoker>& jsInvoker) {
    if (value.isNull() || value.isUndefined()) {
      return {};
    }
    return bridging::fromJs<T>(rt, value, jsInvoker);
  }

  template <typename U>
  static std::optional<T> fromJs(
      jsi::Runtime& rt,
      const std::optional<U>& value,
      const std::shared_ptr<CallInvoker>& jsInvoker) {
    if (value) {
      return bridging::fromJs<T>(rt, *value, jsInvoker);
    }
    return {};
  }

  static jsi::Value toJs(
      jsi::Runtime& rt,
      const std::optional<T>& value,
      const std::shared_ptr<CallInvoker>& jsInvoker) {
    if (value) {
      return bridging::toJs(rt, *value, jsInvoker);
    }
    return jsi::Value::null();
  }
};

template <typename T>
struct Bridging<
    std::shared_ptr<T>,
    std::enable_if_t<!std::is_base_of_v<jsi::HostObject, T>>> {
  static jsi::Value toJs(
      jsi::Runtime& rt,
      const std::shared_ptr<T>& ptr,
      const std::shared_ptr<CallInvoker>& jsInvoker) {
    if (ptr) {
      return bridging::toJs(rt, *ptr, jsInvoker);
    }
    return jsi::Value::null();
  }
};

template <typename T>
struct Bridging<std::unique_ptr<T>> {
  static jsi::Value toJs(
      jsi::Runtime& rt,
      const std::unique_ptr<T>& ptr,
      const std::shared_ptr<CallInvoker>& jsInvoker) {
    if (ptr) {
      return bridging::toJs(rt, *ptr, jsInvoker);
    }
    return jsi::Value::null();
  }
};

template <typename T>
struct Bridging<std::weak_ptr<T>> {
  static jsi::Value toJs(
      jsi::Runtime& rt,
      const std::weak_ptr<T>& weakPtr,
      const std::shared_ptr<CallInvoker>& jsInvoker) {
    if (auto ptr = weakPtr.lock()) {
      return bridging::toJs(rt, *ptr, jsInvoker);
    }
    return jsi::Value::null();
  }
};

} // namespace facebook::react
