#include "GoapRunnerSystem.hpp"
#include "SmartObjectSystem.hpp"
#include "BehaviorTreeSystem.hpp"
#include "AnimationTreeSystem.hpp"
#include "NavMeshSystem.hpp"
#include "../components/GoapPlanner.hpp"
#include "../components/GoapRunner.hpp"
#include "../components/ActionDatabase.hpp"
#include "../components/ActionDebug.hpp"
#include "../components/SmartObject.hpp"
#include "../components/PathFollowing.hpp"
#include "../components/Transform.hpp"
#include "../components/Character.hpp"
#include "../components/NavMesh.hpp"
#include "../EditorApp.hpp"
#include <OgreLogManager.h>
#include <cmath>

GoapRunnerSystem::GoapRunnerSystem(flecs::world &world,
					   Ogre::SceneManager *sceneMgr,
					   SmartObjectSystem *soSystem,
					   BehaviorTreeSystem *btSystem,
					   NavMeshSystem *navSystem)
	: m_world(world)
	, m_sceneMgr(sceneMgr)
	, m_soSystem(soSystem)
	, m_btSystem(btSystem)
	, m_navSystem(navSystem)
{
}

GoapRunnerSystem::~GoapRunnerSystem() = default;

flecs::entity GoapRunnerSystem::findNearestSmartObject(
	flecs::entity character, const Ogre::String &actionName,
	float maxDistance)
{
	if (!character.is_alive() || !character.has<TransformComponent>())
		return flecs::entity::null();

	Ogre::Vector3 charPos =
		character.get<TransformComponent>().node
			->_getDerivedPosition();

	flecs::entity nearest = flecs::entity::null();
	float bestDist = maxDistance;

	m_world.query<SmartObjectComponent, TransformComponent>()
		.each([&](flecs::entity so, SmartObjectComponent &soComp,
			  TransformComponent &soTrans) {
			bool hasAction = false;
			for (const auto &name : soComp.actionNames) {
				if (name == actionName) {
					hasAction = true;
					break;
				}
			}
			if (!hasAction)
				return;

			Ogre::Vector3 soPos =
				soTrans.node->_getDerivedPosition();
			float dist = charPos.distance(soPos);
			if (dist < bestDist) {
				bestDist = dist;
				nearest = so;
			}
		});

	return nearest;
}

bool GoapRunnerSystem::startNextAction(flecs::entity e)
{
	if (!e.has<GoapPlannerComponent>() || !e.has<GoapRunnerComponent>())
		return false;

	auto &planner = e.get_mut<GoapPlannerComponent>();
	auto &runner = e.get_mut<GoapRunnerComponent>();

	if (runner.currentActionIndex >= (int)runner.planActions.size()) {
		// Plan complete
		Ogre::LogManager::getSingleton().logMessage(
			"[GoapRunner] Plan complete.");
		runner.planActions.clear();
		runner.currentActionIndex = 0;
		runner.currentActionName.clear();
		runner.state = GoapRunnerComponent::State::Idle;

		// Clear PathFollowing target
		if (e.has<PathFollowingComponent>()) {
			auto &pf = e.get_mut<PathFollowingComponent>();
			pf.hasTarget = false;
			pf.currentLocomotionState = "idle";
			pf.path.clear();
			pf.pathIndex = 0;
		}
		return false;
	}

	const Ogre::String &actionName =
		runner.planActions[runner.currentActionIndex];
	runner.currentActionName = actionName;

	// Find action database singleton
	ActionDatabase *db = ActionDatabase::getSingletonPtr();

	const GoapAction *action = db ? db->findAction(actionName) : nullptr;

	if (action) {
		// Normal action: run behavior tree via ActionDebug
		runner.state = GoapRunnerComponent::State::RunningAction;

		if (!e.has<ActionDebug>()) {
			e.set<ActionDebug>({});
			m_managedActionDebugs.insert(e.id());
		}
		auto &debug = e.get_mut<ActionDebug>();
		debug.isRunning = true;
		debug.runTimer = 0.0f;
		debug.currentActionName = actionName;
		debug.selectedActionName = actionName;

		Ogre::LogManager::getSingleton().logMessage(
			"[GoapRunner] Starting normal action: " + actionName);
	} else {
		// Smart object action: pathfind and set up PathFollowingComponent
		flecs::entity so = findNearestSmartObject(
			e, actionName, planner.smartObjectDistance);
		if (so.is_alive()) {
			runner.state =
				GoapRunnerComponent::State::MovingToSmartObject;
			runner.targetSmartObjectId = so.id();

			if (e.has<PathFollowingComponent>()) {
				auto &pf = e.get_mut<PathFollowingComponent>();
				auto &soTrans = so.get<TransformComponent>();
				pf.targetPosition =
					soTrans.node->_getDerivedPosition();
				pf.hasTarget = true;
				pf.path.clear();
				pf.pathIndex = 0;
				pf.pathRecalcTimer = 0.0f;

				// Find path via navmesh
				if (m_navSystem) {
					flecs::entity navmeshEntity =
						flecs::entity::null();
					m_world
						.query<NavMeshComponent>()
						.each([&](flecs::entity ne,
							  NavMeshComponent &) {
							if (!navmeshEntity.is_alive())
								navmeshEntity = ne;
						});
					if (navmeshEntity.is_alive()) {
						auto &charTrans =
							e.get<TransformComponent>();
						bool found = m_navSystem->findPath(
							navmeshEntity,
							charTrans.node
								->_getDerivedPosition(),
							pf.targetPosition,
							pf.path);
						if (!found || pf.path.empty()) {
							// Fallback: direct path
							pf.path.clear();
							pf.path.push_back(
								pf.targetPosition);
						}
					} else {
						pf.path.clear();
						pf.path.push_back(pf.targetPosition);
					}
				} else {
					pf.path.clear();
					pf.path.push_back(pf.targetPosition);
				}

				// Choose walk or run based on distance
				auto &charTrans = e.get<TransformComponent>();
				float dist = charTrans.node
						     ->_getDerivedPosition()
						     .distance(pf.targetPosition);
				if (dist > pf.walkSpeed * 3.0f)
					pf.currentLocomotionState = "run";
				else
					pf.currentLocomotionState = "walk";
			}

			Ogre::LogManager::getSingleton().logMessage(
				"[GoapRunner] Starting smart object action: " +
				actionName);
		} else {
			Ogre::LogManager::getSingleton().logMessage(
				"[GoapRunner] No smart object found for action: " +
				actionName);
			// Skip this action
			runner.currentActionIndex++;
			return startNextAction(e);
		}
	}

	return true;
}

void GoapRunnerSystem::update(float deltaTime)
{
	bool shouldRun = true;
	if (m_editorApp) {
		if (m_editorApp->getGameMode() == EditorApp::GameMode::Game &&
		    m_editorApp->getGamePlayState() !=
			    EditorApp::GamePlayState::Playing) {
			shouldRun = false;
		}
	}
	if (!shouldRun)
		return;

	m_world.query<GoapPlannerComponent, GoapRunnerComponent>()
		.each([&](flecs::entity e, GoapPlannerComponent &planner,
			  GoapRunnerComponent &runner) {
			// If idle and plans available, pop one and start
			if (runner.state ==
				    GoapRunnerComponent::State::Idle &&
			    planner.hasPlans()) {
				auto plan = planner.popCheapestPlan();
				runner.planActions = std::move(plan.actions);
				runner.currentActionIndex = 0;
				runner.currentActionName.clear();
				startNextAction(e);
				return;
			}

			// If idle and no plans, mark planner dirty for replanning
			if (runner.state ==
				    GoapRunnerComponent::State::Idle &&
			    !planner.hasPlans()) {
				if (!runner.planActions.empty()) {
					// Just finished a plan batch, need more
					runner.planActions.clear();
					runner.currentActionIndex = 0;
				}
				planner.planDirty = true;
				return;
			}

			// If still idle, nothing to do
			if (runner.state ==
				    GoapRunnerComponent::State::Idle)
				return;

			// Update action timer
			runner.actionTimer += deltaTime;

			// Handle smart object navigation completion
			if (runner.state == GoapRunnerComponent::State::
						      MovingToSmartObject) {
				if (e.has<PathFollowingComponent>()) {
					auto &pf = e.get<PathFollowingComponent>();
					if (!pf.hasTarget) {
						// PathFollowingSystem has reached the
						// destination - execute the action
						Ogre::LogManager::getSingleton()
							.logMessage(
								"[GoapRunner] Reached smart object, executing: " +
								runner.currentActionName);

						// Apply action effects
						if (e.has<GoapBlackboard>()) {
							ActionDatabase *db = ActionDatabase::getSingletonPtr();
							if (db) {
								const GoapAction *action =
									db->findAction(
										runner.currentActionName);
								if (action) {
									auto &bb = e.get_mut<GoapBlackboard>();
									bb.apply(action->effects);
								}
							}
						}

						// Advance to next action
						runner.currentActionIndex++;
						runner.actionTimer = 0.0f;
						runner.targetSmartObjectId = 0;
						startNextAction(e);
					}
				}
				return;
			}

			// Check if current normal action is complete
			if (runner.state == GoapRunnerComponent::State::
						      RunningAction &&
			    m_btSystem && e.has<ActionDebug>()) {
				auto &debug = e.get_mut<ActionDebug>();
				debug.runTimer += deltaTime;

				auto &btState =
					m_btSystem->getActionDebugState(e.id());
				if (debug.runTimer > 0.5f &&
				    btState.treeResult !=
					    BehaviorTreeSystem::Status::running) {
					debug.isRunning = false;
					runner.currentActionIndex++;
					runner.actionTimer = 0.0f;
					startNextAction(e);
				}
			}
		});
}
