/*-
 * ~~~~~~licensing~~~~~~
 * object-producing-resources-support
 * ==========
 * 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.resources.generator;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.entaxy.esb.platform.runtime.base.connecting.generator.AbstractSelfPublishGenerator;
import ru.entaxy.esb.platform.runtime.base.connecting.generator.Generated;
import ru.entaxy.esb.platform.runtime.base.connecting.generator.Generator;
import ru.entaxy.esb.platform.runtime.base.connecting.generator.GeneratorService;
import ru.entaxy.esb.platform.runtime.base.connecting.generator.factory.GeneratorFactory;
import ru.entaxy.esb.resources.EntaxyResource;
import ru.entaxy.esb.resources.EntaxyResourceService;
import ru.entaxy.platform.core.producer.resources.EntaxyResourceProducingProcessorService;
import ru.entaxy.platform.core.producer.resources.ResourceDataProcessor;
import ru.entaxy.platform.core.producer.resources.ResourceDescriptor;
import ru.entaxy.platform.core.producer.resources.ResourceProducer;
import ru.entaxy.platform.core.producer.resources.StringFormatterService;

@Component(
        service = {GeneratorService.class},
        property = {GeneratorService.PROP_GENERATOR_TYPE + "=" + ResourceWrapGenerator.GENERATOR_NAME},
        immediate = true)
public class ResourceWrapGenerator extends AbstractSelfPublishGenerator<ResourceWrapGenerator>
        implements Generator {

    private static final Logger LOG = LoggerFactory.getLogger(ResourceWrapGenerator.class);

    public static final String GENERATOR_NAME = "resource-wrap";

    protected static EntaxyResourceService ENTAXY_RESOURCE_SERVICE;

    protected static StringFormatterService STRING_FORMATTER_SERVICE;

    protected static EntaxyResourceProducingProcessorService RESOURCE_PROCESSOR_SERVICE;

    @Reference(cardinality = ReferenceCardinality.MANDATORY)
    EntaxyResourceService entaxyResourceService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY)
    StringFormatterService stringFormatterService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY)
    EntaxyResourceProducingProcessorService entaxyResourceProcessorService;

    @Activate
    public void activate() {
        GeneratorFactory.register(ResourceWrapGenerator.GENERATOR_NAME, this);
        ResourceWrapGenerator.ENTAXY_RESOURCE_SERVICE = entaxyResourceService;
        ResourceWrapGenerator.STRING_FORMATTER_SERVICE = stringFormatterService;
        ResourceWrapGenerator.RESOURCE_PROCESSOR_SERVICE = entaxyResourceProcessorService;
    }

    @Deactivate
    public void deactivate() {
        GeneratorFactory.unregister(GENERATOR_NAME);
    }

    @Override
    public Generated generate(Map<String, Object> properties) throws Exception {
        ResourceDescriptor descriptor = ResourceDescriptor.fromMap(properties);
        String originContent = null;
        String object = null;
        String finalContent = null;
        if (properties.containsKey(ResourceProducer.PROP_CONTENT)) {
            originContent = properties.get(ResourceProducer.PROP_CONTENT).toString();
            String[] formatters = descriptor.format.split(",");
            ArrayUtils.reverse(formatters);
            object = STRING_FORMATTER_SERVICE.unformat(originContent, formatters);
            if (properties.containsKey(ResourceProducer.PROP_PREPROCESS)) {
                Object pp = properties.get(ResourceProducer.PROP_PREPROCESS);
                if (pp instanceof List) {
                    List list = (List) pp;
                    for (Object obj : list) {
                        if (obj instanceof Map) {

                            Map<String, Object> processorParameters = new HashMap<>(properties);

                            // add context properties as direct keys
                            Object context = properties.get(ResourceProducer.PROP_RESOURCE_CONTEXT);
                            if ((context != null) && (context instanceof Map)) {
                                processorParameters.putAll((Map<String, Object>) context);
                            }

                            processorParameters.putAll((Map<String, Object>) obj);

                            Object result = RESOURCE_PROCESSOR_SERVICE.process(object, processorParameters);
                            if (result != null)
                                object = result.toString();
                            else
                                object = "";
                        }
                    }
                }
            }
            finalContent = object;
        } else {
            EntaxyResource resource = ENTAXY_RESOURCE_SERVICE.getResource(descriptor.getResourceUrl());
            if (resource.exists()) {
                object = resource.getAsString();
                originContent = STRING_FORMATTER_SERVICE.format(object, descriptor.format.split(","));
                finalContent = originContent;
            }
        }
        if (object == null)
            return null;

        // postprocess
        if (properties.containsKey(ResourceProducer.PROP_POSTPROCESS)) {
            Object pp = properties.get(ResourceProducer.PROP_POSTPROCESS);
            LOG.debug("POSTPROCESS for properties:\n {}", properties);
            if (pp instanceof List) {
                List list = (List) pp;
                for (Object obj : list) {
                    if (obj instanceof Map) {
                        Map<String, Object> processorParameters = new HashMap<>(properties);

                        // add context properties as direct keys
                        Object context = properties.get(ResourceProducer.PROP_RESOURCE_CONTEXT);
                        if ((context != null) && (context instanceof Map)) {
                            processorParameters.putAll((Map<String, Object>) context);
                        }

                        processorParameters.putAll((Map<String, Object>) obj);

                        LOG.debug("POSTPROCESS with parameters:\n {}", processorParameters);

                        Object result = RESOURCE_PROCESSOR_SERVICE.process(object, processorParameters);
                        if (result != null)
                            object = result.toString();
                        else
                            object = "";
                    }
                }
            }
        }

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.putAll(properties);
        resultMap.put(ResourceProducer.PROP_ORIGIN_CONTENT, originContent);
        resultMap.put(ResourceProducer.PROP_CONTENT, finalContent);
        resultMap.put("#_object", object);
        return new GeneratedResourceWrapper(resultMap);
    }

    @Override
    public boolean isGeneratable(Map<String, Object> properties) throws Exception {
        return properties.containsKey(ResourceDataProcessor.PROP_PROVIDER)
                && properties.containsKey(ResourceDataProcessor.PROP_LOCATION);
    }

}
