#include "RuntimeLayerController.h"

#include "RuntimeJson.h"
#include "../logging/Logger.h"

namespace RenderCadenceCompositor
{
ControlActionResult RuntimeLayerController::HandleAddLayer(const std::string& body)
{
	CleanupRetiredShaderBuilds();

	std::string shaderId;
	std::string error;
	if (!ExtractStringField(body, "shaderId", shaderId, error))
		return { false, error };

	std::string layerId;
	{
		std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
		if (!mRuntimeLayerModel.AddLayer(mShaderCatalog, shaderId, layerId, error))
			return { false, error };
		RequestRuntimeStatePersistenceLocked();
	}

	Log("runtime-shader", "Layer added: " + layerId + " shader=" + shaderId);
	StartLayerShaderBuild(layerId, shaderId);
	return { true, std::string() };
}

ControlActionResult RuntimeLayerController::HandleRemoveLayer(const std::string& body)
{
	CleanupRetiredShaderBuilds();

	std::string layerId;
	std::string error;
	if (!ExtractStringField(body, "layerId", layerId, error))
		return { false, error };

	{
		std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
		if (!mRuntimeLayerModel.RemoveLayer(layerId, error))
			return { false, error };
		RequestRuntimeStatePersistenceLocked();
	}

	Log("runtime-shader", "Layer removed: " + layerId);
	RetireLayerShaderBuild(layerId);
	PublishRuntimeRenderLayers();
	return { true, std::string() };
}

ControlActionResult RuntimeLayerController::HandleControlCommand(const RuntimeControlCommand& command)
{
	CleanupRetiredShaderBuilds();

	std::string error;
	switch (command.type)
	{
	case RuntimeControlCommandType::AddLayer:
	{
		std::string layerId;
		{
			std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
			if (!mRuntimeLayerModel.AddLayer(mShaderCatalog, command.shaderId, layerId, error))
				return { false, error };
			RequestRuntimeStatePersistenceLocked();
		}
		Log("runtime-shader", "Layer added: " + layerId + " shader=" + command.shaderId);
		StartLayerShaderBuild(layerId, command.shaderId);
		return { true, std::string() };
	}
	case RuntimeControlCommandType::RemoveLayer:
	{
		{
			std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
			if (!mRuntimeLayerModel.RemoveLayer(command.layerId, error))
				return { false, error };
			RequestRuntimeStatePersistenceLocked();
		}
		Log("runtime-shader", "Layer removed: " + command.layerId);
		RetireLayerShaderBuild(command.layerId);
		PublishRuntimeRenderLayers();
		return { true, std::string() };
	}
	case RuntimeControlCommandType::ReorderLayer:
	{
		{
			std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
			if (!mRuntimeLayerModel.ReorderLayer(command.layerId, command.targetIndex, error))
				return { false, error };
			RequestRuntimeStatePersistenceLocked();
		}
		PublishRuntimeRenderLayers();
		return { true, std::string() };
	}
	case RuntimeControlCommandType::SetLayerBypass:
	{
		{
			std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
			if (!mRuntimeLayerModel.SetLayerBypass(command.layerId, command.bypass, error))
				return { false, error };
			RequestRuntimeStatePersistenceLocked();
		}
		PublishRuntimeRenderLayers();
		return { true, std::string() };
	}
	case RuntimeControlCommandType::SetLayerShader:
	{
		{
			std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
			if (!mRuntimeLayerModel.SetLayerShader(mShaderCatalog, command.layerId, command.shaderId, error))
				return { false, error };
			RequestRuntimeStatePersistenceLocked();
		}
		Log("runtime-shader", "Layer shader changed: " + command.layerId + " shader=" + command.shaderId);
		StartLayerShaderBuild(command.layerId, command.shaderId);
		return { true, std::string() };
	}
	case RuntimeControlCommandType::UpdateLayerParameter:
	{
		{
			std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
			if (!mRuntimeLayerModel.UpdateParameter(command.layerId, command.parameterId, command.value, error))
				return { false, error };
			RequestRuntimeStatePersistenceLocked();
		}
		PublishRuntimeRenderLayers();
		return { true, std::string() };
	}
	case RuntimeControlCommandType::ResetLayerParameters:
	{
		{
			std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
			if (!mRuntimeLayerModel.ResetParameters(command.layerId, error))
				return { false, error };
			RequestRuntimeStatePersistenceLocked();
		}
		PublishRuntimeRenderLayers();
		return { true, std::string() };
	}
	case RuntimeControlCommandType::ReloadShaders:
	{
		if (!LoadSupportedShaderCatalog(mShaderLibrary, mMaxTemporalHistoryFrames))
			return { false, "Shader reload failed; see logs for details." };

		std::vector<std::pair<std::string, std::string>> buildsToStart;
		{
			std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
			if (!mRuntimeLayerModel.ReloadFromCatalog(mShaderCatalog, buildsToStart, error))
				return { false, error };
			RequestRuntimeStatePersistenceLocked();
		}

		for (const auto& build : buildsToStart)
		{
			Log("runtime-shader", "Reload queued shader rebuild: " + build.first + " shader=" + build.second);
			StartLayerShaderBuild(build.first, build.second, true);
		}
		return { true, std::string() };
	}
	case RuntimeControlCommandType::Unsupported:
		break;
	}

	return { false, "Unsupported runtime control command." };
}

bool RuntimeLayerController::ExtractStringField(const std::string& body, const char* fieldName, std::string& value, std::string& error)
{
	JsonValue root;
	std::string parseError;
	if (!ParseJson(body.empty() ? "{}" : body, root, parseError) || !root.isObject())
	{
		error = parseError.empty() ? "Request body must be a JSON object." : parseError;
		return false;
	}

	const JsonValue* field = root.find(fieldName);
	if (!field || !field->isString() || field->asString().empty())
	{
		error = std::string("Request field '") + fieldName + "' must be a non-empty string.";
		return false;
	}

	value = field->asString();
	error.clear();
	return true;
}
}
