#ifndef SRC_NODE_MODULES_H_
#define SRC_NODE_MODULES_H_

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "node.h"
#include "node_snapshotable.h"
#include "simdjson.h"
#include "util.h"
#include "v8-fast-api-calls.h"
#include "v8.h"

#include <filesystem>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>

namespace node {
class ExternalReferenceRegistry;

namespace modules {

class BindingData : public SnapshotableObject {
 public:
  using InternalFieldInfo = InternalFieldInfoBase;

  struct PackageConfig {
    std::string file_path;
    std::optional<std::string> name;
    std::optional<std::string> main;
    std::string type = "none";
    std::optional<std::string> exports;
    std::optional<std::string> imports;
    std::optional<std::string> scripts;
    std::string raw_json;

    v8::Local<v8::Array> Serialize(Realm* realm) const;
  };

  struct ErrorContext {
    std::optional<std::string> base;
    std::string specifier;
    bool is_esm;
  };

  BindingData(Realm* realm,
              v8::Local<v8::Object> obj,
              InternalFieldInfo* info = nullptr);
  SERIALIZABLE_OBJECT_METHODS()
  SET_BINDING_ID(modules_binding_data)

  void MemoryInfo(MemoryTracker* tracker) const override;
  SET_SELF_SIZE(BindingData)
  SET_MEMORY_INFO_NAME(BindingData)

  static void ReadPackageJSON(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void GetNearestParentPackageJSON(
      const v8::FunctionCallbackInfo<v8::Value>& args);
  static void GetNearestParentPackageJSONType(
      const v8::FunctionCallbackInfo<v8::Value>& args);
  template <bool return_only_type>
  static void GetPackageScopeConfig(
      const v8::FunctionCallbackInfo<v8::Value>& args);
  static void GetPackageJSONScripts(
      const v8::FunctionCallbackInfo<v8::Value>& args);

  static void CreatePerIsolateProperties(IsolateData* isolate_data,
                                         v8::Local<v8::ObjectTemplate> ctor);
  static void CreatePerContextProperties(v8::Local<v8::Object> target,
                                         v8::Local<v8::Value> unused,
                                         v8::Local<v8::Context> context,
                                         void* priv);
  static void RegisterExternalReferences(ExternalReferenceRegistry* registry);

 private:
  std::unordered_map<std::string, PackageConfig> package_configs_;
  simdjson::ondemand::parser json_parser;
  // returns null on error
  static const PackageConfig* GetPackageJSON(
      Realm* realm,
      std::string_view path,
      ErrorContext* error_context = nullptr);
  static const PackageConfig* TraverseParent(
      Realm* realm, const std::filesystem::path& check_path);
};

}  // namespace modules
}  // namespace node

#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#endif  // SRC_NODE_MODULES_H_
