/*-
 * ~~~~~~licensing~~~~~~
 * object-producer-core
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.platform.core.producer.executor.generationmodel;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import ru.entaxy.platform.base.objects.factory.EntaxyFactory;
import ru.entaxy.platform.base.support.JSONUtils;
import ru.entaxy.platform.core.producer.executor.objectmodel.FactoredObject;
import ru.entaxy.platform.core.producer.executor.objectmodel.FactoredObjectRef;

public class GeneratedObjectsPublishDataStorage {

    Map<String, JsonObject> publishData = new HashMap<>();

    public JsonObject getPublishData(String objectUuid) {
        return publishData.getOrDefault(objectUuid, new JsonObject());
    }

    public JsonObject generatePublishData(FactoredObject factoredObject, EntaxyFactory factory, String outputType) {

        JsonObject result = new JsonObject();
        JsonObject originObject = factoredObject.origin;

        result = getFilteredObject(originObject, originObject, factory, outputType);

        publishData.put(factoredObject.getUuid(), result);

        return getPublishData(factoredObject.getUuid());
    }

    protected JsonObject getFilteredObject(JsonObject originObject, JsonObject rootObject, EntaxyFactory factory,
            String outputType) {

        JsonObject result = new JsonObject();

        Map<String, JsonObject> factoryFields = factory.getOutputByType(outputType).getFields().stream()
                .collect(Collectors.toMap(fi -> fi.getName(), fi -> fi.getJsonOrigin()));

        for (Entry<String, JsonElement> entry : originObject.entrySet()) {
            String name = entry.getKey();

            if (FactoredObject.EMBEDDED_FIELD.equals(name) || FactoredObject.IGNORE_SECTION.equals(name)
                    || (name.startsWith("##") && !name.startsWith("##config")))
                continue;

            boolean skip = false;

            // check @INTERNAL directive via factory data
            JsonObject joField = factoryFields.getOrDefault(name, new JsonObject());
            if (joField.has(EntaxyFactory.CONFIGURATION.DIRECTIVES.INTERNAL)) {
                try {
                    skip = joField.get(EntaxyFactory.CONFIGURATION.DIRECTIVES.INTERNAL).getAsBoolean();
                } catch (Exception ignore) {
                    // NOOP
                }
            }
            if (skip)
                continue;

            // check @INTERNAL directive via object data
            JsonElement je = entry.getValue();
            if (je.isJsonObject()) {
                JsonObject jo = je.getAsJsonObject();

                if (jo.has(EntaxyFactory.CONFIGURATION.DIRECTIVES.INTERNAL)) {
                    try {
                        skip = jo.get(EntaxyFactory.CONFIGURATION.DIRECTIVES.INTERNAL).getAsBoolean();
                    } catch (Exception e) {
                        // NOOP
                    }
                    if (skip)
                        continue;
                }
            }

            // check for @PUBLISH_UNRESOLVED directive
            if (joField.has(GenerationModel.DIRECTIVE_PUBLISH_URESOLVED)) {

                JsonElement publishUnresolved = joField.get(GenerationModel.DIRECTIVE_PUBLISH_URESOLVED);
                String publishUnresolvedValue = publishUnresolved.getAsString();

                boolean publishRef = publishUnresolvedValue.equalsIgnoreCase(GenerationModel.PUBLISH_UNRESOLVED_REF);
                boolean publishObject =
                        publishUnresolvedValue.equalsIgnoreCase(GenerationModel.PUBLISH_UNRESOLVED_OBJECT);

                if (publishRef || publishObject) {

                    JsonObject unresolvedRef = null;

                    // first we must get unresolved ref
                    if (je.isJsonObject()) {
                        if (FactoredObjectRef.isRef(je.getAsJsonObject())) {
                            // ref is found
                            unresolvedRef = je.getAsJsonObject();
                        }
                    }
                    if (unresolvedRef == null) {
                        JsonElement unresolved = JSONUtils.findElementExt(rootObject,
                                String.format("%s.properties.%s", GenerationModel.UNRESOLVED_CONTAINER_NAME, name));
                        if (unresolved != null && unresolved.isJsonObject())
                            unresolvedRef = unresolved.getAsJsonObject();
                    }

                    if (unresolvedRef != null) {
                        if (publishRef) {
                            result.add(name, unresolvedRef);
                            continue;
                        } else {

                            // get target object
                            JsonElement uuid = unresolvedRef.get(FactoredObjectRef.TARGET_UUID_FIELD);
                            if (uuid.isJsonPrimitive()) {
                                String objectUuid = uuid.getAsString();
                                if (publishData.containsKey(objectUuid)) {
                                    JsonObject toPublish = publishData.get(objectUuid);
                                    // we nust remove 'isEmbedded'
                                    toPublish.remove(FactoredObject.IS_EMBEDDED_FIELD);
                                    result.add(name, toPublish);
                                    continue;
                                }
                            }

                        }
                    }

                }

            }

            if (je.isJsonObject()) {
                JsonObject filtered = getFilteredObject(je.getAsJsonObject(), rootObject, factory, outputType);
                if (filtered != null)
                    result.add(name, filtered);
            } else {
                result.add(name, je);
            }


        }
        return result;

    }

}
