#include <frostbite2D/script/quickjs_bridge_base.h>

#include <SDL2/SDL.h>
#include <frostbite2D/script/quickjs_vm.h>

#include <quickjspp.hpp>

#include <exception>
#include <utility>

namespace frostbite2D {

std::shared_ptr<JsActor> JsActor::GetParent() const {
  if (!actor_ || !actor_->GetParent()) {
    return nullptr;
  }

  return std::make_shared<JsActor>(Ptr<Actor>(actor_->GetParent()));
}

std::shared_ptr<JsVec2> JsActor::GetTopLeftPosition() const {
  if (!actor_) {
    return std::make_shared<JsVec2>(0.0, 0.0);
  }

  return std::make_shared<JsVec2>(actor_->GetTopLeftPosition());
}

void JsActor::SetTopLeftPosition(const std::shared_ptr<JsVec2>& position) {
  if (actor_ && position) {
    actor_->SetTopLeftPosition(position->toNative());
  }
}

void JsActor::SetTopLeftPositionXY(double x, double y) {
  if (actor_) {
    actor_->SetTopLeftPosition(static_cast<float>(x), static_cast<float>(y));
  }
}

std::shared_ptr<JsVec2> JsActor::LocalToWorld(
    const std::shared_ptr<JsVec2>& point) const {
  if (!actor_ || !point) {
    return std::make_shared<JsVec2>(0.0, 0.0);
  }

  return std::make_shared<JsVec2>(actor_->LocalToWorld(point->toNative()));
}

std::shared_ptr<JsVec2> JsActor::WorldToLocal(
    const std::shared_ptr<JsVec2>& point) const {
  if (!actor_ || !point) {
    return std::make_shared<JsVec2>(0.0, 0.0);
  }

  return std::make_shared<JsVec2>(actor_->WorldToLocal(point->toNative()));
}

bool JsActor::ContainsWorldPoint(double x, double y) const {
  return actor_ && actor_->ContainsWorldPoint(static_cast<float>(x),
                                              static_cast<float>(y));
}

uint32 JsActor::AddEventListener(const std::string& type,
                                 std::function<bool(qjs::Value)> callback) {
  if (!actor_ || !callback) {
    return 0;
  }

  QuickJSVM& vm = QuickJSVM::get();
  auto eventType = vm.parseEventTypeName(type);
  if (!eventType) {
    SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
                "[QuickJS] Unsupported event type for AddEventListener: %s",
                type.c_str());
    return 0;
  }

  actor_->EnableEventReceive();
  return actor_->AddEventListener(
      *eventType, [callback = std::move(callback)](const Event& event) -> bool {
        QuickJSVM& vm = QuickJSVM::get();
        if (!vm.isInitialized() || !vm.getContext()) {
          return false;
        }

        try {
          qjs::Value eventValue = vm.wrapEvent(event);
          if (eventValue == JS_UNDEFINED || eventValue == JS_NULL) {
            return false;
          }

          bool handled = callback(std::move(eventValue));
          if (!vm.runPendingJobs()) {
            return false;
          }
          return handled;
        } catch (qjs::exception) {
          vm.logException("JsActor::AddEventListener callback failed");
        } catch (const std::exception& e) {
          SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
                       "JsActor::AddEventListener runtime error: %s",
                       e.what());
        }

        return false;
      });
}

bool JsActor::RemoveEventListener(uint32 listenerId) {
  return actor_ && actor_->RemoveEventListener(listenerId);
}

void JsActor::ClearEventListeners() {
  if (actor_) {
    actor_->ClearEventListeners();
  }
}

} // namespace frostbite2D
