package org.openlca.jsonld.upgrades;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.openlca.core.model.ModelType;
import org.openlca.jsonld.Json;
import org.openlca.jsonld.JsonStoreReader;

import java.util.HashMap;
import java.util.List;
import java.util.Objects;

class Upgrade2 extends Upgrade {

	private List<JsonObject> _rawImpactMethods;
	private List<JsonObject> _rawNwSets;

	Upgrade2(JsonStoreReader reader) {
		super(reader);
	}

	@Override
	public JsonObject get(ModelType type, String refId) {
		var object = super.get(type, refId);
		if (object == null)
			return null;
		if (type == ModelType.IMPACT_METHOD) {
			inlineNwSets(object);
		}
		if (type == ModelType.IMPACT_CATEGORY) {
			addImpactCategoryParams(object);
		}
		return object;
	}

	private void inlineNwSets(JsonObject methodObj) {
		if (methodObj == null)
			return;

		var nwRefs = Json.getArray(methodObj, "nwSets");
		if (nwRefs == null || nwRefs.isEmpty())
			return;

		if (_rawNwSets == null) {
			_rawNwSets = super.getFiles("nw_sets").stream()
				.map(super::getJson)
				.filter(Objects::nonNull)
				.filter(JsonElement::isJsonObject)
				.map(JsonElement::getAsJsonObject)
				.toList();
		}

		var idx = new HashMap<String, JsonObject>();
		for (var nwSet : _rawNwSets) {
			var id = Json.getString(nwSet, "@id");
			if (id == null)
				continue;
			idx.put(id, nwSet);
		}
		if (idx.isEmpty())
			return;

		var nwSets = new JsonArray();
		Json.stream(nwRefs)
			.filter(JsonElement::isJsonObject)
			.map(ref -> Json.getString(ref.getAsJsonObject(), "@id"))
			.filter(Objects::nonNull)
			.map(idx::get)
			.filter(Objects::nonNull)
			.map(JsonObject::deepCopy)
			.forEach(nwSets::add);
		methodObj.add("nwSets", nwSets);
	}

	private void addImpactCategoryParams(JsonObject object) {
		var id = Json.getString(object, "@id");
		if (id == null)
			return;

		// check if there are already parameters
		var params = Json.getArray(object, "parameters");
		if (params != null)
			return;

		// copy possible method parameters into the impact category
		var methodObj = getMethodOfImpact(id);
		if (methodObj != null) {
			var methodParams = Json.getArray(methodObj, "parameters");
			var impactParams = new JsonArray();
			if (methodParams != null) {
				// TODO: maybe update the parameters
				Json.stream(methodParams)
					.filter(JsonElement::isJsonObject)
					.map(JsonElement::getAsJsonObject)
					.map(JsonObject::deepCopy)
					.forEach(impactParams::add);
			}
		}
	}

	private JsonObject getMethodOfImpact(String impactId) {

		if (_rawImpactMethods == null) {
			_rawImpactMethods = super.getRefIds(ModelType.IMPACT_METHOD)
				.stream()
				.map(methodId -> super.get(ModelType.IMPACT_METHOD, methodId))
				.filter(Objects::nonNull)
				.toList();
		}

		for (var methodObj : _rawImpactMethods) {
			var impRefs = Json.getArray(methodObj, "impactCategories");
			if (impRefs == null)
				continue;
			for (var impRef : impRefs) {
				if (!impRef.isJsonObject())
					continue;
				var impId = Json.getString(impRef.getAsJsonObject(), "@id");
				if (Objects.equals(impactId, impId))
					return methodObj;
			}
		}
		return null;
	}

}

@@ -796,24 +796,95 @@
 bject);%0A%09%09%7D%0A
+%09%09if (type == ModelType.PRODUCT_SYSTEM) %7B%0A%09%09%09addRedefSets(object);%0A%09%09%7D%0A
 %09%09return obj
@@ -3277,10 +3277,728 @@
 ll;%0A%09%7D%0A%0A
+%09private void addRedefSets(JsonObject systemObj) %7B%0A%0A%09%09// check of there are already paramater sets%0A%09%09var currentSets = Json.getArray(systemObj, %22parameterSets%22);%0A%09%09if (currentSets != null)%0A%09%09%09return;%0A%0A%09%09var params = Json.getArray(systemObj, %22parameters%22);%0A%09%09if (params == null)%0A%09%09%09return;%0A%0A%09%09var set = new JsonObject();%0A%09%09set.addProperty(%22name%22, %22Baseline%22);%0A%09%09set.addProperty(%22isBaseline%22, true);%0A%09%09var redefs = new JsonArray();%0A%09%09Json.stream(params)%0A%09%09%09%09.filter(JsonElement::isJsonObject)%0A%09%09%09.map(JsonElement::getAsJsonObject)%0A%09%09%09.map(JsonObject::deepCopy)%0A%09%09%09.forEach(redefs::add);%0A%09%09set.add(%22parameters%22, redefs);%0A%0A%09%09var redefSets = new JsonArray();%0A%09%09redefSets.add(set);%0A%09%09systemObj.add(%22parameterSets%22, set);%0A%09%7D%0A
 %7D%0A

add redef-sets