#include "PrefabSystem.hpp"
#include "SceneSerializer.hpp"
#include "EditorUISystem.hpp"
#include "../components/PrefabInstance.hpp"
#include "../components/Transform.hpp"
#include "../components/EntityName.hpp"
#include "../components/EditorMarker.hpp"
#include <OgreLogManager.h>
#include <filesystem>

std::string PrefabSystem::getPrefabsDirectory()
{
	return "prefabs";
}

PrefabSystem::PrefabSystem(flecs::world &world, Ogre::SceneManager *sceneMgr)
	: m_world(world)
	, m_sceneMgr(sceneMgr)
{
}

void PrefabSystem::resolveInstances()
{
	m_world.query<PrefabInstanceComponent>().each(
		[&](flecs::entity entity, PrefabInstanceComponent &prefab) {
			if (prefab.instantiated || prefab.prefabPath.empty())
				return;

			SceneSerializer serializer(m_world, m_sceneMgr);
			if (serializer.instantiatePrefab(entity,
							 prefab.prefabPath)) {
				prefab.instantiated = true;
			} else {
				m_lastError = serializer.getLastError();
				Ogre::LogManager::getSingleton().logMessage(
					"PrefabSystem: Failed to instantiate '" +
					prefab.prefabPath +
					"': " + m_lastError);
			}
		});
}

flecs::entity PrefabSystem::createInstance(const std::string &prefabPath,
					   flecs::entity parent,
					   const Ogre::Vector3 &position,
					   const std::string &name,
					   EditorUISystem *uiSystem)
{
	// Read prefab root transform so we can preserve it as the base
	Ogre::Vector3 prefabPos(0, 0, 0);
	Ogre::Quaternion prefabRot(Ogre::Quaternion::IDENTITY);
	Ogre::Vector3 prefabScale(1, 1, 1);
	try {
		std::ifstream file(prefabPath);
		if (file.is_open()) {
			nlohmann::json prefabJson;
			file >> prefabJson;
			file.close();
			if (prefabJson.contains("transform")) {
				auto &t = prefabJson["transform"];
				if (t.contains("position")) {
					auto &p = t["position"];
					prefabPos = Ogre::Vector3(
						p.value("x", 0.0f),
						p.value("y", 0.0f),
						p.value("z", 0.0f));
				}
				if (t.contains("rotation")) {
					auto &r = t["rotation"];
					prefabRot = Ogre::Quaternion(
						r.value("w", 1.0f),
						r.value("x", 0.0f),
						r.value("y", 0.0f),
						r.value("z", 0.0f));
				}
				if (t.contains("scale")) {
					auto &s = t["scale"];
					prefabScale = Ogre::Vector3(
						s.value("x", 1.0f),
						s.value("y", 1.0f),
						s.value("z", 1.0f));
				}
			}
		}
	} catch (...) {
		// Ignore read errors — defaults are safe
	}

	flecs::entity instance = m_world.entity();
	instance.add<EditorMarkerComponent>();
	instance.set<EntityNameComponent>(EntityNameComponent(name));
	instance.set<PrefabInstanceComponent>(
		PrefabInstanceComponent{ prefabPath, false });

	// Create transform: caller offset + prefab root transform
	TransformComponent transform;
	Ogre::SceneNode *parentNode = m_sceneMgr->getRootSceneNode();
	if (parent.is_valid() && parent != 0 &&
	    parent.has<TransformComponent>()) {
		parentNode = parent.get<TransformComponent>().node;
		instance.child_of(parent);
	}
	transform.node = parentNode->createChildSceneNode();
	transform.position = position + prefabPos;
	transform.rotation = prefabRot;
	transform.scale = prefabScale;
	transform.applyToNode();
	instance.set<TransformComponent>(transform);

	Ogre::LogManager::getSingleton().logMessage(
		"PrefabSystem::createInstance: name=" + name +
		" offset=" + Ogre::StringConverter::toString(position) +
		" prefabPos=" + Ogre::StringConverter::toString(prefabPos) +
		" finalPos=" +
		Ogre::StringConverter::toString(
			transform.node->_getDerivedPosition()) +
		" parent=" +
		(parent.is_valid() && parent != 0 ?
			 std::to_string(parent.id()) :
			 "<root>") +
		" nodeParent=" +
		(parentNode != m_sceneMgr->getRootSceneNode() ?
			 parentNode->getName() :
			 "<root>"));

	if (uiSystem)
		uiSystem->addEntity(instance);

	// Instantiate prefab
	SceneSerializer serializer(m_world, m_sceneMgr);
	if (serializer.instantiatePrefab(instance, prefabPath, uiSystem)) {
		auto &prefab = instance.get_mut<PrefabInstanceComponent>();
		prefab.instantiated = true;
	} else {
		m_lastError = serializer.getLastError();
		Ogre::LogManager::getSingleton().logMessage(
			"PrefabSystem: Failed to create instance of '" +
			prefabPath + "': " + m_lastError);
	}

	return instance;
}

bool PrefabSystem::savePrefab(flecs::entity rootEntity,
			      const std::string &prefabPath)
{
	SceneSerializer serializer(m_world, m_sceneMgr);
	if (!serializer.savePrefab(rootEntity, prefabPath)) {
		m_lastError = serializer.getLastError();
		return false;
	}
	return true;
}

bool PrefabSystem::deletePrefab(const std::string &prefabPath)
{
	try {
		if (!std::filesystem::exists(prefabPath)) {
			m_lastError = "Prefab file not found: " + prefabPath;
			return false;
		}
		if (!std::filesystem::remove(prefabPath)) {
			m_lastError = "Failed to delete prefab: " + prefabPath;
			return false;
		}
		Ogre::LogManager::getSingleton().logMessage(
			"PrefabSystem: Deleted prefab '" + prefabPath + "'");
		return true;
	} catch (const std::exception &e) {
		m_lastError =
			std::string("Failed to delete prefab: ") + e.what();
		return false;
	}
}
