/*-
 * ~~~~~~licensing~~~~~~
 * test-producers
 * ==========
 * 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.base.objects.factory;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections4.MapUtils;

import com.google.gson.JsonObject;

import ru.entaxy.esb.platform.runtime.base.connecting.generator.Generated;

public interface EntaxyFactory extends EntaxyFactoryElements.EntaxyFactoryExtendedElement {

    /*
     * public constants, enums etc
     */

    public static interface SERVICE {

        String PROP_ID = "factory.id";
        String PROP_TYPE = "factory.type";
        String PROP_ORIGIN_BUNDLE = "factory.origin.bundle";

    }

    public static interface CONFIGURATION {

        String FACTORY_SECTION_NAME = "factory";
        String FIELDS_SECTION_NAME = "fields";
        String OUTPUTS_SECTION_NAME = "outputs";

        public static interface FACTORY {

            String ID = "id";
            String TYPE = "type";

            String SHORT_NAME = "shortName";
            String DISPLAY_NAME = "displayName";
            String DESCRIPTION = "description";
            String LABEL = "label";
            String CATEGORY = "category";

            String PARENT = "parent";
            String REQUIRES = "requires";

            String IS_ABSTRACT = "isAbstract";
            String IS_DEPRECATED = "isDeprecated";

            String IS_INTERNAL = "isInternal";
        }

        public static interface FIELDS {

            public static interface REGULAR_TYPES {

                String STRING = "String";
                String BOOLEAN = "Boolean";
                String LONG = "Long";
                String DOUBLE = "Double";

                public static Set<String> asSet() {
                    return new HashSet<>() {
                        {
                            add(STRING);
                            add(BOOLEAN);
                            add(DOUBLE);
                            add(LONG);
                        }
                    };
                }

            }

            public static interface ATTRIBUTES {

                String FIELD_TYPE = "type";

                String FIELD_DISPLAY_NAME = "displayName";
                String FIELD_DESCRIPTION = "description";

                String DEFAULT_VALUE = "defaultValue";

                String REQUIRED = "required";
                String IMMUTABLE = "immutable";

                String IS_REF = "isRef";

                String ADD_TO_OUTPUT = "addToOutput";

            }

            String OBJECT_ID = "objectId";

        }

        public static interface OUTPUTS {

            String OUTPUT_TYPE_INIT = "init";
            String OUTPUT_TYPE_REF = "ref";

            public static interface ATTRIBUTES {

                String IS_DEFAULT = "isDefault";
                String CONFIG = "config";
                String GENERATOR = "generator";
                String FIELDS = "fields";
                String SCOPES = "scopes";

            }
        }

        public static interface DIRECTIVES {

            String OVERRIDE = "@OVERRIDE";
            String INHERIT = "@INHERIT";
            String IMPORT = "@IMPORT";
            String VARIANTS = "@VARIANTS";
            String CALCULATED = "@CALCULATED";
            String SCOPED = "@SCOPED";
            String INTERNAL = "@INTERNAL";
            String NO_LOOKUP = "@NO_LOOKUP";
            String LOCALS = "@LOCALS";

            static Set<String> getAllDirectives() {
                return new HashSet<String>() {

                    private static final long serialVersionUID = 1L;

                    {
                        add(OVERRIDE);
                        add(IMPORT);
                        add(VARIANTS);
                    }
                };
            }

            static Set<String> designDirectives = new HashSet<>(Arrays.asList(
                    new String[] {
                        OVERRIDE, INHERIT, IMPORT, VARIANTS, SCOPED, LOCALS}));

            static Set<String> getDesignDirectives() {
                return designDirectives;
            }


            public static enum OVERRIDE_MODE {

                // leave parent value, no changes made
                // parent value is 'final'
                IGNORE("ignore"),

                // child value is used
                // parent value is ignored
                REPLACE("replace"),

                // use parent value
                // with addition of new elements from child
                APPEND("append"),

                // use parent value updated from child
                // with addition of new elements from child
                UPDATE("update");

                public String label;

                private OVERRIDE_MODE(String label) {
                    this.label = label;
                }

                public static OVERRIDE_MODE valueOfLabel(String label) {
                    for (OVERRIDE_MODE e : values()) {
                        if (e.label.equalsIgnoreCase(label)) {
                            return e;
                        }
                    }
                    return null;
                }

            }

            /**
             * Mode for array data inheritance
             * 
             * @author Serge
             *
             */
            public static enum INHERIT_MODE {

                // leave parent value, no changes made
                // parent value is 'final'
                IGNORE("ignore"),

                // child value is used
                // parent value is ignored
                REPLACE("replace"),

                // use parent value
                // with addition of new elements from child
                APPEND("append"),

                // use parent value
                // with addition of new elements from child in the beginning
                PREPEND("prepend");

                public String label;

                private INHERIT_MODE(String label) {
                    this.label = label;
                }

                public static INHERIT_MODE valueOfLabel(String label) {
                    for (INHERIT_MODE e : values()) {
                        if (e.label.equalsIgnoreCase(label)) {
                            return e;
                        }
                    }
                    return null;
                }

            }


        }

    }

    public static interface SEARCH {

        String ATTR_ID = CONFIGURATION.FACTORY.ID;
        String ATTR_TYPE = CONFIGURATION.FACTORY.TYPE;
        String ATTR_PARENT = CONFIGURATION.FACTORY.PARENT;

        String ATTR_SHORT_NAME = CONFIGURATION.FACTORY.SHORT_NAME;
        String ATTR_DISPLAY_NAME = CONFIGURATION.FACTORY.DISPLAY_NAME;
        String ATTR_DESCRIPTION = CONFIGURATION.FACTORY.DESCRIPTION;
        String ATTR_LABEL = CONFIGURATION.FACTORY.LABEL;
        String ATTR_CATEGORY = CONFIGURATION.FACTORY.CATEGORY;

        String ATTR_REQUIRES = CONFIGURATION.FACTORY.REQUIRES;

        String ATTR_IS_ABSTRACT = CONFIGURATION.FACTORY.IS_ABSTRACT;
        String ATTR_IS_DEPRECATED = CONFIGURATION.FACTORY.IS_DEPRECATED;

        String ATTR_IS_INTERNAL = CONFIGURATION.FACTORY.IS_INTERNAL;

        String ATTR_TYPEINFO_PREFIX = "typeinfo.";
        String ATTR_TYPEINFO = ATTR_TYPEINFO_PREFIX + "*";

        List<String> STANDARD_ATTRIBUTES_LIST = Arrays.asList(new String[] {
            ATTR_ID,
            ATTR_TYPE,
            ATTR_PARENT,

            ATTR_SHORT_NAME,
            ATTR_DISPLAY_NAME,
            ATTR_DESCRIPTION,
            ATTR_LABEL,
            ATTR_CATEGORY,

            ATTR_REQUIRES,

            ATTR_IS_ABSTRACT,
            ATTR_IS_DEPRECATED,
            ATTR_IS_INTERNAL,

            ATTR_TYPEINFO

        });

        Map<String, String> STANDARD_ATTRIBUTES = MapUtils.putAll(new LinkedHashMap<>(), new String[][] {
            {ATTR_ID, "Factory Id"},
            {ATTR_TYPE, "Factory type"},
            {ATTR_PARENT, "Parent factory Id"},

            {ATTR_SHORT_NAME, "Factory short name"},
            {ATTR_DISPLAY_NAME, "Factory display name"},
            {ATTR_DESCRIPTION, "Descrption"},
            {ATTR_LABEL, "Label"},
            {ATTR_CATEGORY, "Category"},

            {ATTR_REQUIRES, "Requirements"},

            {ATTR_IS_ABSTRACT, "Is factory abstract"},
            {ATTR_IS_DEPRECATED, "Is factory deprecated"},
            {ATTR_IS_INTERNAL, "Is factory internal"},

            {ATTR_TYPEINFO, "Specific type information"}
        });
    }

    public static interface GENERATION {

        public static interface ATTRIBUTES {

            String OUTPUT = "generation.output";
            String SCOPE = "generation.scope";
            String FACTORY = "generation.factory";

        }

    }

    static public enum SCOPE {
        PUBLIC("public"),
        PROTECTED("protected"),
        PRIVATE("private");

        public String label;

        private SCOPE(String label) {
            this.label = label;
        }

        public static SCOPE valueOfLabel(String label) {
            for (SCOPE e : values()) {
                if (e.label.equals(label)) {
                    return e;
                }
            }
            return null;
        }
    }

    static public SCOPE DEFAULT_SCOPE = SCOPE.PUBLIC;

    /*
     * inner structure elements
     */

    public static interface OutputInfo {

        public String getType();

        public String getGenerator();

        public List<String> getScopes();

        public Map<String, Object> getConfig();

        public List<FieldInfo> getFields();

        public boolean isDefault();

    }

    public static interface FieldInfo {

        public String getName();

        public String getType();

        public String getDisplayName();

        public String getDescription();

        public boolean isImmutable();

        public boolean isRequired();

        public Object getDefaultValue();

        public boolean isConditional();

        public String getCondition();

        public boolean isRef();

        public JsonObject getJsonOrigin();

    }

    public static interface RefFieldInfo extends FieldInfo {

        public boolean isBackRef();

        public boolean isRefByValueOnly();

        public String getRefField();

    }

    /*
     * Factory itself
     */

    @Deprecated()
    public default String getFactoryId() {
        return getId();
    };

    @Deprecated
    public default String getFactoryType() {
        return getType();
    };

    public String getShortName();

    public boolean isAbstract();

    public boolean isInternal();

    public boolean isDeprecated();

    public String getParent();

    public List<OutputInfo> getOutputs();

    public OutputInfo getDefaultOutput();

    public OutputInfo getOutputByType(String outputType);

    public List<FieldInfo> getFields();

    public List<FieldInfo> getFields(String outputType);

    public Map<String, Object> getTypeInfo();

    public String getJsonConfiguration();

    public String getJsonOrigin();

    public Generated generate(Map<String, Object> parameters) throws EntaxyFactoryException;

    public Generated generate(String outputType, Map<String, Object> parameters) throws EntaxyFactoryException;

    public Generated generate(String outputType, String scope, Map<String, Object> parameters)
            throws EntaxyFactoryException;

}
