#include "EventHandlerSystem.hpp"
#include "../components/EventParams.hpp"
#include "../components/EventHandler.hpp"
#include <OgreLogManager.h>

// Forward declare the BehaviorTreeSystem interface we need
class BehaviorTreeSystem {
public:
	virtual ~BehaviorTreeSystem() = default;
	virtual bool startAction(flecs::entity entity,
				 const Ogre::String &actionName) = 0;
	virtual bool isActionRunning(flecs::entity entity) const = 0;
	virtual void stopAction(flecs::entity entity) = 0;
};

EventHandlerSystem::EventHandlerSystem(flecs::world &world,
				       BehaviorTreeSystem *btSystem)
	: m_world(world)
	, m_btSystem(btSystem)
{
}

EventHandlerSystem::~EventHandlerSystem()
{
	// Unsubscribe all active subscriptions
	for (const auto &pair : m_subscriptions) {
		EventBus::getInstance().unsubscribe(pair.second);
	}
	m_subscriptions.clear();
}

void EventHandlerSystem::update(float deltaTime)
{
	// Process active handlers
	auto it = m_activeHandlers.begin();
	while (it != m_activeHandlers.end()) {
		ActiveHandler &handler = it->second;

		if (handler.firstFrame) {
			handler.firstFrame = false;

			// Start the behavior tree action
			flecs::entity e(m_world, handler.entityId);
			if (e.is_alive()) {
				// Inject event params into the entity's
				// blackboard
				std::unordered_set<std::string> keys;
				injectParams(e, handler.eventParams, keys);
				m_injectedKeys[handler.entityId] = keys;

				// Start the action
				if (m_btSystem) {
					m_btSystem->startAction(
						e, handler.actionName);
				}
			}
		}

		// Check if the action is still running
		bool stillRunning = false;
		if (m_btSystem) {
			flecs::entity e(m_world, handler.entityId);
			if (e.is_alive()) {
				stillRunning = m_btSystem->isActionRunning(e);
			}
		}

		if (!stillRunning) {
			// Cleanup injected params
			auto keyIt = m_injectedKeys.find(handler.entityId);
			if (keyIt != m_injectedKeys.end()) {
				flecs::entity e(m_world, handler.entityId);
				if (e.is_alive()) {
					removeParams(e, keyIt->second);
				}
				m_injectedKeys.erase(keyIt);
			}

			it = m_activeHandlers.erase(it);
		} else {
			++it;
		}
	}
}

void EventHandlerSystem::subscribeEntity(
	flecs::entity e, const struct EventHandlerComponent &handler)
{
	if (!e.is_alive())
		return;

	flecs::entity_t id = e.id();

	// Unsubscribe existing subscription if any
	auto existing = m_subscriptions.find(id);
	if (existing != m_subscriptions.end()) {
		EventBus::getInstance().unsubscribe(existing->second);
	}

	// Subscribe to the event
	EventBus::ListenerId listenerId = EventBus::getInstance().subscribe(
		handler.eventName,
		[this, id](const Ogre::String &eventName,
			   const editScene::EventParams &params) {
			onEvent(id, eventName, params);
		});

	m_subscriptions[id] = listenerId;
}

void EventHandlerSystem::unsubscribeEntity(flecs::entity_t id)
{
	auto it = m_subscriptions.find(id);
	if (it != m_subscriptions.end()) {
		EventBus::getInstance().unsubscribe(it->second);
		m_subscriptions.erase(it);
	}

	// Clean up active handlers
	m_activeHandlers.erase(id);

	// Clean up injected keys
	auto keyIt = m_injectedKeys.find(id);
	if (keyIt != m_injectedKeys.end()) {
		flecs::entity e(m_world, id);
		if (e.is_alive()) {
			removeParams(e, keyIt->second);
		}
		m_injectedKeys.erase(keyIt);
	}
}

void EventHandlerSystem::onEvent(flecs::entity_t entityId,
				 const Ogre::String &eventName,
				 const editScene::EventParams &params)
{
	// Check if entity is still alive
	flecs::entity e(m_world, entityId);
	if (!e.is_alive())
		return;

	// Check if there's already an active handler for this entity
	if (m_activeHandlers.find(entityId) != m_activeHandlers.end()) {
		Ogre::LogManager::getSingleton().stream()
			<< "EventHandlerSystem: Entity " << entityId
			<< " already has an active handler for event "
			<< eventName;
		return;
	}

	// Look up the EventHandlerComponent to get the action name
	if (!e.has<EventHandlerComponent>()) {
		Ogre::LogManager::getSingleton().stream()
			<< "EventHandlerSystem: Entity " << entityId
			<< " has no EventHandlerComponent";
		return;
	}

	const EventHandlerComponent &handlerComp =
		e.get<EventHandlerComponent>();

	// Create active handler
	ActiveHandler handler;
	handler.entityId = entityId;
	handler.actionName = handlerComp.actionName;
	handler.eventParams = params;
	handler.firstFrame = true;

	m_activeHandlers[entityId] = handler;
}

void EventHandlerSystem::injectParams(flecs::entity e,
				      const editScene::EventParams &params,
				      std::unordered_set<std::string> &outKeys)
{
	// Get or create the GoapBlackboard component
	// For now, we just log the params being injected
	Ogre::LogManager::getSingleton().stream()
		<< "EventHandlerSystem: Injecting params for entity " << e.id()
		<< ":\n"
		<< params.dump();

	// In a full implementation, we would inject each param into
	// the entity's GoapBlackboard. For now, we track the keys.
	for (editScene::EventParams::ConstIterator it = params.begin();
	     it != params.end(); ++it) {
		outKeys.insert(it->first);
	}
}

void EventHandlerSystem::removeParams(
	flecs::entity e, const std::unordered_set<std::string> &keys)
{
	Ogre::LogManager::getSingleton().stream()
		<< "EventHandlerSystem: Removing params for entity " << e.id();
}
