#include "Utils.h"

#include <Misc.h>
#include <fstream>

#if defined(__APPLE__) || defined(__ANDROID__)
#include <filesystem>
namespace fs = std::__fs::filesystem;
#elif defined(_WIN32)
#include <filesystem>
namespace fs = std::filesystem;
#endif

namespace sharedjsi
{

Function readFile(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    std::string filePath = arguments[0].asString(runtime).utf8(runtime);
    try {
      std::ifstream fileStream(filePath, std::ios::binary);
      if (!fileStream.is_open()) {
        throw std::runtime_error("Cannot open file: " + filePath);
      }

      // Read the file into a vector
      std::vector<uint8_t> buffer((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
      return TypedArray<TypedArrayKind::Uint8Array>(runtime, buffer);
    } catch (const std::exception& e) {
      throw JSError(runtime, e.what());
    }
  });
}

Function copyFile(Runtime &runtime) {
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime, STRINGIFIED_FUNC_NAME()),
                                          3,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value {
    std::string sourcePath = arguments[0].asString(runtime).utf8(runtime);
    std::string destinationPath = arguments[1].asString(runtime).utf8(runtime);
    std::string defaultFolder = arguments[2].asString(runtime).utf8(runtime);

    std::string finalDestinationPath = destinationPath;
    if (destinationPath[0] != '/') {
      finalDestinationPath = defaultFolder + "/" + destinationPath;
    }

    fs::path destinationDirectory = fs::path(finalDestinationPath).parent_path();
    if (!fs::exists(destinationDirectory)) {
      try {
        fs::create_directories(destinationDirectory);
      } catch (const fs::filesystem_error& e) {
        throw JSError(runtime, ("Error creating directory for file copy: " + std::string(e.what())).c_str());
      }
    }

    if (fs::exists(finalDestinationPath)) {
      throw JSError(runtime, ("Destination file already exists and will not be overwritten: " + finalDestinationPath).c_str());
    }

    try {
      fs::copy(sourcePath, finalDestinationPath);
    } catch (const fs::filesystem_error& e) {
      throw JSError(runtime, ("Error copying file: " + std::string(e.what())).c_str());
    }

    return {};
  });
}

Function createDirectory(Runtime &runtime, String &defaultDirPath)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          1,
                                          [defaultDirPathStr = defaultDirPath.utf8(runtime)](Runtime &runtime,
                                                                                             const Value &thisValue,
                                                                                             const Value *arguments,
                                                                                             size_t count) -> Value
                                          {
    std::string path = arguments[0].asString(runtime).utf8(runtime);

    std::string finalPath = path;
    if (path.empty() || path[0] != '/') {
      finalPath = defaultDirPathStr + "/" + path;
    }

    fs::path p(finalPath);
    if (!fs::exists(p)) {
      try {
        fs::create_directories(p);
      } catch (const std::exception &e) {
        throw JSError(runtime, "Error creating directory: " + std::string(e.what()));
      }
    }

    return String::createFromUtf8(runtime, finalPath);
  });
}

Function dittoffi_ditto_absolute_persistence_directory(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          1,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ditto = jsPointerObjectToCPointer<CDitto_t>(runtime, arguments[0]);
    char* absolute_path = ::dittoffi_ditto_absolute_persistence_directory(ditto);
    return cPointerToJSPointerObject<char>(runtime, absolute_path);

  });
}

}
