/*-
 * ~~~~~~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.wrapper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.CollectionType;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.entaxy.platform.base.objects.factory.EntaxyFactory;
import ru.entaxy.platform.base.support.CommonUtils;
import ru.entaxy.platform.core.producer.wrapper.AbstractFactoryWrapper.GenerationProcessor;

@Component(service = GenerationProcessorService.class, immediate = true)
public class GenerationProcessorService {
	
	private static final Logger log = LoggerFactory.getLogger(GenerationProcessorService.class);
	
	public static final String FACTORY_PLUGIN_FIELD = "generation.plugins";
	
	private static GenerationProcessorService INSTANCE;
	
	public static GenerationProcessorService getInstance() {
		return INSTANCE;
	}
	
	protected static enum PLUGIN_STATUS {
		DENIED,
		ALLOWED,
		UNDEFINED
	}
	
	protected static class GenerationProcessorHelper {
		
		ServiceReference<GenerationProcessor> ref;
		
		String id;
		List<String> factoryTypes = new ArrayList<>();
		
		public GenerationProcessorHelper(ServiceReference<GenerationProcessor> ref) {
			this.ref = ref;
			
			this.id = getId(ref);
			if (!CommonUtils.isValid(this.id))
				return;
			
			Object obj = ref.getProperty(GenerationProcessor.PROP_FACTORY_TYPE);
			
			if (obj == null)
				return;
			
			if (obj instanceof String) {
				factoryTypes.add(obj.toString());
				return;
			}
			
			if (obj instanceof String[]) {
				String[] data = (String[])obj;
				factoryTypes.addAll(Arrays.asList(data));
			}

			if (obj instanceof List) {
				for (Object item: (List)obj)
					if (item!=null)
						factoryTypes.add(item.toString());
			}
		}
		
		public static String getId(ServiceReference<GenerationProcessor> ref) {
			Object obj = ref.getProperty(GenerationProcessor.PROP_PROCESSOR_ID); 
			if (obj != null)
				return obj.toString();
			return null;
			
		}
		
		public PLUGIN_STATUS getStatusForType(String factoryType) {
			
			PLUGIN_STATUS result = PLUGIN_STATUS.UNDEFINED; 
			
			for (String type: factoryTypes) {
				boolean denied = type.startsWith("!");
				if (denied)
					type = type.substring(1);
				
				if (matches(factoryType, type)) {
					if (denied)
						return PLUGIN_STATUS.DENIED;
					else
						result = PLUGIN_STATUS.ALLOWED;
				}
			}
			
			return result;
		}
		
		protected boolean matches(String value, String template) {
			
			String expression = template.replaceAll("\\*", ".*");
			return Pattern.matches(expression, value);
			
		}
		
	}

	Map<String, GenerationProcessorHelper> processors = new HashMap<>();
	
	@Activate
	public void activate() {
		INSTANCE = this;
	}
	
	@Reference (unbind = "removeProcessor", cardinality = ReferenceCardinality.MULTIPLE
			, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY
			, service = GenerationProcessor.class, collectionType = CollectionType.REFERENCE)
	public void addProcessor(ServiceReference<GenerationProcessor> ref) {
		String id = GenerationProcessorHelper.getId(ref);
		if (!CommonUtils.isValid(id))
			return;
		processors.put(id, new GenerationProcessorHelper(ref));
	}
	
	public void removeProcessor(ServiceReference<GenerationProcessor> ref) {
		String id = GenerationProcessorHelper.getId(ref);
		if (!CommonUtils.isValid(id))
			return;
		processors.remove(id);
	}

	protected PLUGIN_STATUS getEffectiveStatus(PLUGIN_STATUS st1, PLUGIN_STATUS st2) {
		List<PLUGIN_STATUS> list = new ArrayList<>() {{
			add(st1);
			add(st2);
		}};
		if (list.contains(PLUGIN_STATUS.DENIED))
			return PLUGIN_STATUS.DENIED;
		if (list.contains(PLUGIN_STATUS.ALLOWED))
			return PLUGIN_STATUS.ALLOWED;
		return PLUGIN_STATUS.UNDEFINED;
	}
	
	public List<ServiceReference<GenerationProcessor>> getProcessorsRefs(EntaxyFactory factory){
		List<ServiceReference<GenerationProcessor>> result = new ArrayList<>();
		
		Map<String, PLUGIN_STATUS> factoryData = getStatusFromFactory(factory);
		String type = factory.getType();
		
		for (String pluginId: processors.keySet()) {
			GenerationProcessorHelper helper = processors.get(pluginId);
			PLUGIN_STATUS status = getEffectiveStatus(factoryData.get(pluginId)
					, helper.getStatusForType(type));
			if (PLUGIN_STATUS.ALLOWED.equals(status))
				result.add(helper.ref);
			
		}
		
		return result;
	}
	
	protected Map<String, PLUGIN_STATUS> getStatusFromFactory(EntaxyFactory factory) {
		
		Map<String, PLUGIN_STATUS> result = new HashMap<>();
		
		Map<String, Object> typeInfo = factory.getTypeInfo();
		if (typeInfo.containsKey(FACTORY_PLUGIN_FIELD)) {
			List<String> plugins = new ArrayList<>();
			Object obj = typeInfo.get(FACTORY_PLUGIN_FIELD);
			if (obj != null) {
				if (obj instanceof String)
					plugins.add(obj.toString());
				else if (obj instanceof Object[]) {
					List<Object> objList = Arrays.asList((Object[])obj);
					for (Object item: objList)
						if (item!=null)
							plugins.add(item.toString());
				} else if (obj instanceof List)
					for (Object item: (List)obj)
						if (item != null)
							plugins.add(item.toString());
			}
			for (String plugin: plugins) {
				if (plugin.startsWith("!")) {
					plugin = plugin.substring(1);
					result.put(plugin, PLUGIN_STATUS.DENIED);
				} else {
					result.put(plugin, PLUGIN_STATUS.ALLOWED);
				}
			}
		}
		
		return result;
	}
	
}
