#ifndef EDITSCENE_SCENESERIALIZER_HPP
#define EDITSCENE_SCENESERIALIZER_HPP

#pragma once
#include <flecs.h>
#include <Ogre.h>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>

// Forward declarations
class EditorUISystem;

/**
 * Scene serializer for saving/loading scenes in JSON format
 */
class SceneSerializer {
public:
	SceneSerializer(flecs::world &world, Ogre::SceneManager *sceneMgr);

	/**
	 * Save scene to JSON file
	 */
	bool saveToFile(const std::string &filepath);

	/**
	 * Load scene from JSON file
	 */
	bool loadFromFile(const std::string &filepath,
			  EditorUISystem *uiSystem = nullptr);

	/**
	 * Get last error message
	 */
	const std::string &getLastError() const
	{
		return m_lastError;
	}

	/**
	 * Save an entity subtree as a prefab JSON file.
	 */
	bool savePrefab(flecs::entity rootEntity, const std::string &filepath);

	/**
	 * Instantiate a prefab onto an existing entity.
	 *
	 * The prefab root components are copied onto instanceEntity,
	 * except transform (the instance entity's transform is the
	 * world-space override).  Prefab children are created as
	 * children of instanceEntity without EditorMarkerComponent.
	 *
	 * @param instanceEntity Existing entity with PrefabInstanceComponent.
	 * @param filepath Path to prefab JSON file.
	 * @param uiSystem Optional UI system for registering new entities.
	 * @return true on success.
	 */
	bool instantiatePrefab(flecs::entity instanceEntity,
			       const std::string &filepath,
			       EditorUISystem *uiSystem = nullptr);

	/**
	 * Load a prefab file into the scene as editable entities.
	 *
	 * Unlike instantiatePrefab(), this creates a brand-new root
	 * entity WITH EditorMarkerComponent so it can be edited and
	 * saved back with savePrefab().
	 *
	 * @param filepath Path to prefab JSON file.
	 * @param uiSystem Optional UI system for registering new entities.
	 * @return The created root entity, or null on failure.
	 */
	flecs::entity loadPrefabForEdit(const std::string &filepath,
					EditorUISystem *uiSystem = nullptr);

private:
	// Serialization helpers
	void deserializeEntity(const nlohmann::json &json, flecs::entity parent,
			       EditorUISystem *uiSystem);

	// First-pass deserialization: creates entities and populates entity map
	// without resolving material references (deferred to second pass)
	void deserializeEntityFirstPass(const nlohmann::json &json,
					flecs::entity parent,
					EditorUISystem *uiSystem);

	// Resolve procedural texture references for ProceduralMaterial components
	// (must run after all entities are loaded).
	void resolveProceduralTextureReferences();

	// Second-pass: resolve material entity references for Town, District, Lot
	void resolveMaterialReferences();

	// Component serialization
	nlohmann::json serializeTransform(flecs::entity entity);
	nlohmann::json serializeRenderable(flecs::entity entity);
	nlohmann::json serializeEntityName(flecs::entity entity);
	nlohmann::json serializeRigidBody(flecs::entity entity);
	nlohmann::json serializeCollider(flecs::entity entity);
	nlohmann::json serializeLight(flecs::entity entity);
	nlohmann::json serializeCamera(flecs::entity entity);
	nlohmann::json serializeLodSettings(flecs::entity entity);
	nlohmann::json serializeLod(flecs::entity entity);
	nlohmann::json serializeStaticGeometry(flecs::entity entity);
	nlohmann::json serializeStaticGeometryMember(flecs::entity entity);
	nlohmann::json serializeProceduralTexture(flecs::entity entity);
	nlohmann::json serializeProceduralMaterial(flecs::entity entity);
	nlohmann::json serializePrimitive(flecs::entity entity);
	nlohmann::json serializeTriangleBuffer(flecs::entity entity);
	nlohmann::json serializeCharacter(flecs::entity entity);
	nlohmann::json serializeCharacterIdentity(flecs::entity entity);
	nlohmann::json serializeCharacterSpawner(flecs::entity entity);
	nlohmann::json serializeCharacterSlots(flecs::entity entity);
	nlohmann::json serializeCharacterShapeKeys(flecs::entity entity);
	nlohmann::json serializeAnimationTree(flecs::entity entity);
	nlohmann::json serializeAnimationTreeTemplate(flecs::entity entity);
	nlohmann::json serializeStartupMenu(flecs::entity entity);
	nlohmann::json serializePlayerController(flecs::entity entity);

	// CellGrid/Town component serialization
	nlohmann::json serializeCellGrid(flecs::entity entity);
	nlohmann::json serializeTown(flecs::entity entity);
	nlohmann::json serializeDistrict(flecs::entity entity);
	nlohmann::json serializeLot(flecs::entity entity);
	nlohmann::json serializeRoom(flecs::entity entity);
	nlohmann::json serializeRoof(flecs::entity entity);
	nlohmann::json serializeFurnitureTemplate(flecs::entity entity);
	nlohmann::json serializeClearArea(flecs::entity entity);
	nlohmann::json serializeNavMesh(flecs::entity entity);
	nlohmann::json serializeNavMeshGeometrySource(flecs::entity entity);

	// Component deserialization
	void deserializeTransform(flecs::entity entity,
				  const nlohmann::json &json,
				  flecs::entity parentEntity);
	void deserializeRenderable(flecs::entity entity,
				   const nlohmann::json &json);
	void deserializeEntityName(flecs::entity entity,
				   const nlohmann::json &json);
	void deserializeRigidBody(flecs::entity entity,
				  const nlohmann::json &json);
	void deserializeCollider(flecs::entity entity,
				 const nlohmann::json &json);
	void deserializeLight(flecs::entity entity, const nlohmann::json &json);
	void deserializeCamera(flecs::entity entity,
			       const nlohmann::json &json);
	void deserializeLodSettings(flecs::entity entity,
				    const nlohmann::json &json);
	void deserializeLod(flecs::entity entity, const nlohmann::json &json);
	void deserializeStaticGeometry(flecs::entity entity,
				       const nlohmann::json &json);
	void deserializeStaticGeometryMember(flecs::entity entity,
					     const nlohmann::json &json);
	void deserializeProceduralTexture(flecs::entity entity,
					  const nlohmann::json &json);
	void deserializeProceduralMaterial(flecs::entity entity,
					   const nlohmann::json &json);
	void deserializePrimitive(flecs::entity entity,
				  const nlohmann::json &json);
	void deserializeTriangleBuffer(flecs::entity entity,
				       const nlohmann::json &json);
	void deserializeCharacter(flecs::entity entity,
				  const nlohmann::json &json);
	void deserializeCharacterIdentity(flecs::entity entity,
					  const nlohmann::json &json);
	void deserializeCharacterSpawner(flecs::entity entity,
					 const nlohmann::json &json);
	void deserializeCharacterSlots(flecs::entity entity,
				       const nlohmann::json &json);
	void deserializeCharacterShapeKeys(flecs::entity entity,
					   const nlohmann::json &json);
	void deserializeAnimationTree(flecs::entity entity,
				      const nlohmann::json &json);
	void deserializeAnimationTreeTemplate(flecs::entity entity,
					      const nlohmann::json &json);
	void deserializeStartupMenu(flecs::entity entity,
				    const nlohmann::json &json);
	void deserializePlayerController(flecs::entity entity,
					 const nlohmann::json &json);

	// CellGrid/Town component deserialization
	void deserializeCellGrid(flecs::entity entity,
				 const nlohmann::json &json);
	void deserializeTown(flecs::entity entity, const nlohmann::json &json);
	void deserializeDistrict(flecs::entity entity,
				 const nlohmann::json &json);
	void deserializeLot(flecs::entity entity, const nlohmann::json &json);
	void deserializeRoom(flecs::entity entity, const nlohmann::json &json);
	void deserializeRoof(flecs::entity entity, const nlohmann::json &json);
	void deserializeFurnitureTemplate(flecs::entity entity,
					  const nlohmann::json &json);
	void deserializeClearArea(flecs::entity entity,
				  const nlohmann::json &json);
	void deserializeNavMesh(flecs::entity entity,
				const nlohmann::json &json);
	void deserializeNavMeshGeometrySource(flecs::entity entity,
					      const nlohmann::json &json);

	// Buoyancy component serialization
	nlohmann::json serializeBuoyancyInfo(flecs::entity entity);
	nlohmann::json serializeWaterPhysics(flecs::entity entity);
	void deserializeBuoyancyInfo(flecs::entity entity,
				     const nlohmann::json &json);
	void deserializeWaterPhysics(flecs::entity entity,
				     const nlohmann::json &json);

	// WaterPlane serialization
	nlohmann::json serializeWaterPlane(flecs::entity entity);
	void deserializeWaterPlane(flecs::entity entity,
				   const nlohmann::json &json);

	// Sun / Skybox serialization
	nlohmann::json serializeSun(flecs::entity entity);
	nlohmann::json serializeSkybox(flecs::entity entity);
	void deserializeSun(flecs::entity entity, const nlohmann::json &json);
	void deserializeSkybox(flecs::entity entity,
			       const nlohmann::json &json);

	// Item/Inventory serialization
	nlohmann::json serializeItem(flecs::entity entity);
	nlohmann::json serializeInventory(flecs::entity entity);
	void deserializeItem(flecs::entity entity, const nlohmann::json &json);
	void deserializeInventory(flecs::entity entity,
				  const nlohmann::json &json);

	// Character class serialization
	nlohmann::json serializeCharacterClass(flecs::entity entity);
	nlohmann::json serializeCharacterClassOverride(flecs::entity entity);
	void deserializeCharacterClass(flecs::entity entity,
				       const nlohmann::json &json);
	void deserializeCharacterClassOverride(flecs::entity entity,
					       const nlohmann::json &json);

	// AI/GOAP serialization
	nlohmann::json serializeActionDatabase();
	nlohmann::json serializeActionDebug(flecs::entity entity);
	nlohmann::json serializePathFollowing(flecs::entity entity);
	nlohmann::json serializeSmartObject(flecs::entity entity);
	nlohmann::json serializeActuator(flecs::entity entity);
	nlohmann::json serializeEventHandler(flecs::entity entity);
	nlohmann::json serializeGoapPlanner(flecs::entity entity);
	nlohmann::json serializeGoapRunner(flecs::entity entity);
	nlohmann::json serializeBehaviorTree(flecs::entity entity);
	void deserializeActionDatabase(const nlohmann::json &json);
	void deserializeActionDebug(flecs::entity entity,
				    const nlohmann::json &json);
	void deserializePathFollowing(flecs::entity entity,
				      const nlohmann::json &json);
	void deserializeSmartObject(flecs::entity entity,
				    const nlohmann::json &json);
	void deserializeActuator(flecs::entity entity,
				 const nlohmann::json &json);
	void deserializeEventHandler(flecs::entity entity,
				     const nlohmann::json &json);
	void deserializeGoapPlanner(flecs::entity entity,
				    const nlohmann::json &json);
	void deserializeGoapRunner(flecs::entity entity,
				   const nlohmann::json &json);
	void deserializeBehaviorTree(flecs::entity entity,
				     const nlohmann::json &json);

	// PrefabInstance serialization
	nlohmann::json serializePrefabInstance(flecs::entity entity);
	void deserializePrefabInstance(flecs::entity entity,
				       const nlohmann::json &json);

	/* --- Public helpers for save/load system --- */
	public:
	/**
	 * Deserialize all components from JSON onto an existing entity.
	 * Used by both scene load and prefab instantiation.
	 *
	 * @param entity Target entity.
	 * @param json Entity JSON.
	 * @param parent flecs parent entity.
	 * @param uiSystem Optional UI system.
	 * @param processTransform If false, skip the "transform" key
	 *        (used when the instance entity already has an override
	 *        TransformComponent).
	 * @param processName If false, skip the "name" key.
	 * @param addEditorMarker If true, children get EditorMarkerComponent.
	 */
	void deserializeEntityComponents(flecs::entity entity,
					 const nlohmann::json &json,
					 flecs::entity parent,
					 EditorUISystem *uiSystem,
					 bool processTransform,
					 bool processName = true,
					 bool addEditorMarker = true);

	/**
	 * Serialize a single entity (with all its components) to JSON.
	 * Public so SaveLoadSystem can serialize runtime entities.
	 */
	nlohmann::json serializeEntity(flecs::entity entity);

	private:

	flecs::world &m_world;
	Ogre::SceneManager *m_sceneMgr;
	std::string m_lastError;

	// Track entity ID mapping for parent/child relationships
	std::unordered_map<uint64_t, flecs::entity> m_entityMap;

	// Pending material entity resolutions (two-pass load)
	// Store entity reference directly (not just ID) so we don't need to look up in m_entityMap
	struct PendingResolution {
		flecs::entity entity;
		nlohmann::json data;
	};
	std::vector<PendingResolution> m_pendingTownResolutions;
	std::vector<PendingResolution> m_pendingDistrictResolutions;
	std::vector<PendingResolution> m_pendingLotResolutions;

	// Pending ProceduralMaterial -> ProceduralTexture resolutions
	std::vector<std::pair<flecs::entity, std::string>>
		m_pendingProceduralTextureResolutions;
};

#endif // EDITSCENE_SCENESERIALIZER_HPP
