#ifndef EDITSCENE_CHARACTERSLOTSYSTEM_HPP
#define EDITSCENE_CHARACTERSLOTSYSTEM_HPP
#pragma once

#include <flecs.h>
#include <Ogre.h>
#include <nlohmann/json.hpp>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "../components/CharacterSlots.hpp"

/**
 * Rich catalog entry for a body part mesh.
 */
struct BodyPartEntry {
	Ogre::String mesh;
	int layer = 0;
	std::vector<Ogre::String> garments;
	std::vector<Ogre::String> tags;
	std::vector<Ogre::String> shapeKeys;
};

/**
 * System that manages multi-slot character meshes with shared skeleton.
 * Loads body part catalog from body_part_*.json files and creates/updates
 * Ogre entities for each slot, sharing the skeleton from the master slot.
 *
 * Character appearance is read from CharacterRegistry (via
 * CharacterIdentityComponent); the deprecated CharacterSlotsComponent is no
 * longer used as a data source.
 */
class CharacterSlotSystem {
public:
	CharacterSlotSystem(flecs::world &world, Ogre::SceneManager *sceneMgr);
	~CharacterSlotSystem();

	void initialize();
	void update();

	/* Singleton access */
	static CharacterSlotSystem *getSingletonPtr();

	/* Static catalog access */
	static void loadCatalog();
	static bool isCatalogLoaded();
	static std::vector<Ogre::String> getAges();
	static std::vector<Ogre::String> getSexes(const Ogre::String &age);
	static std::vector<Ogre::String> getSlots(const Ogre::String &age,
						  const Ogre::String &sex);

	/* Query meshes for a specific layer */
	static std::vector<Ogre::String> getMeshesForLayer(
		const Ogre::String &age, const Ogre::String &sex,
		const Ogre::String &slot, int layer);

	/* Get display label for a catalog entry (garment names joined) */
	static Ogre::String getMeshLabel(const Ogre::String &age,
					 const Ogre::String &sex,
					 const Ogre::String &slot,
					 const Ogre::String &mesh);

	/* Legacy flat list (for editor explicit mesh fallback) */
	static std::vector<Ogre::String> getMeshes(const Ogre::String &age,
						   const Ogre::String &sex,
						   const Ogre::String &slot);

	/* Raw catalog access for systems that need full metadata */
	static const nlohmann::json &getCatalog() { return s_bodyParts; }

	/* Shape key vocabulary for UI */
	static std::vector<Ogre::String> getShapeKeyNames(
		const Ogre::String &age, const Ogre::String &sex);

	/* Resolve a single slot to a mesh name given outfit level */
	static Ogre::String resolveMesh(const Ogre::String &age,
					const Ogre::String &sex,
					const Ogre::String &slot,
					const SlotSelection &sel,
					int outfitLevel);

	/* Mark an entity for rebuild on next update */
	void markEntityDirty(flecs::entity_t id);

	/* Runtime entity accessors */
	Ogre::Entity *getMasterEntity(flecs::entity e);
	Ogre::Entity *getSlotEntity(flecs::entity e,
				    const Ogre::String &slot);
	void setSlotVisible(flecs::entity e, const Ogre::String &slot,
			    bool visible);

private:
	static CharacterSlotSystem *ms_singleton;
	static bool s_catalogLoaded;
	static nlohmann::json s_bodyParts;
	static std::set<Ogre::String> s_meshNames;

	void buildCharacter(flecs::entity e);
	void destroyCharacterParts(flecs::entity e);
	void applyShapeKeys(flecs::entity e, Ogre::Entity *ent,
			    const nlohmann::json *entry);

	flecs::world &m_world;
	Ogre::SceneManager *m_sceneMgr;
	bool m_initialized = false;

	struct PartEntities {
		Ogre::Entity *masterEntity = nullptr;
		std::unordered_map<Ogre::String, Ogre::Entity *> parts;
	};
	std::unordered_map<flecs::entity_t, PartEntities> m_entities;
	std::unordered_set<flecs::entity_t> m_dirtyEntities;
};

#endif // EDITSCENE_CHARACTERSLOTSYSTEM_HPP
