/*-
 * ~~~~~~licensing~~~~~~
 * artifact-management
 * ==========
 * 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.artifact.repository;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.felix.utils.properties.TypedProperties;
import org.apache.karaf.config.core.ConfigRepository;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.entaxy.esb.platform.runtime.core.initializer.api.AbstractInitializer;
import ru.entaxy.esb.platform.runtime.core.initializer.api.InitializerException;
import ru.entaxy.platform.base.support.osgi.OSGIUtils;
import ru.entaxy.platform.core.artifact.service.ArtifactService;

public class ArtifactRepositoryInitializer extends AbstractInitializer {

	private static final Logger log = LoggerFactory.getLogger(ArtifactRepositoryInitializer.class);

	private static final String MVN_PID = "org.ops4j.pax.url.mvn";
	
	private static final String PROP_ENTAXY_REPO_LIST = "org.ops4j.pax.url.mvn.repositories.entaxy";
	
	private static final String ENTAXY_PROPERTY_HTTP_PORT = "entaxy.org.osgi.service.http.port";

	private static final String WEB_CONFIG_PID = "org.ops4j.pax.web";
	private static final String HTTP_ENABLED = "org.osgi.service.http.enabled";

	private ArtifactService artifactService = null;
		
	protected ConfigRepository configRepository;
	
	@Override
	public void init() throws InitializerException {
		ServiceReference<?>[] references = bundleContext.getBundle().getRegisteredServices();
		if (references != null)
			for (int i=0; i<references.length; i++) {
				Object objectClass = references[i].getProperty("objectClass");
				if (objectClass!=null) {
					String[] classes = (String[])objectClass;
					if (Arrays.asList(classes)
							.stream()
							.filter((s)->ArtifactService.class.getName().equals(s))
							.count() > 0) {
						try {
							artifactService = (ArtifactService)bundleContext.getService(references[i]);
							break;
						} catch (Exception e) {
							log.error("Error getting ArtifactService service from ServiceReference");
						}
					};
				};
			}
		if (artifactService == null)
			try {
				artifactService = OSGIUtils.services().ofClass(ArtifactService.class).waitService(20000).get();	
			} catch (Exception e) {
				throw new InitializerException(this, "ArtifactService not found", "", e);
			}
		
		if (artifactService == null) {
			throw new InitializerException(this, "ArtifactService not found", "", null);
		}

		
		List<String> repositoryUrls = new ArrayList<>();
		String urlPrefix = "http" + (isHttpEnabled() ? "" : "s") + "://localhost:${" + ENTAXY_PROPERTY_HTTP_PORT + "}";
		
		// check if 'local' and 'shared' repositories are defined

		ArtifactRepository repository = artifactService.getRepository(ArtifactRepository.REPO_NAME_LOCAL);
		if (!repository.isHealthy() || !repository.isEnabled()) {
			// TODO create own exceptions 
			throw new InitializerException(this
					, String.format("Repository [{}] is not healthy or not enabled", repository.getName())
					, "Please check the configuration", null);
		}
		// don't check sha1 or md5
		repositoryUrls.add(
				urlPrefix + repository.getUrl() 
				+ "@id=" + repository.getName() 
				+ "@checksum=ignore"
				+ (repository.isSnapshotsAllowed()?"@snapshots":"")
			);
		
		repository = artifactService.getRepository(ArtifactRepository.REPO_NAME_SHARED);
		if (!repository.isHealthy() || !repository.isEnabled()) {
			// TODO create own exceptions 
			throw new InitializerException(this
					, String.format("Repository [{}] is not healthy or not enabled", repository.getName())
					, "Please check the configuration", null);
		}
		// don't check sha1 or md5
		repositoryUrls.add(
				urlPrefix + repository.getUrl() 
				+ "@id=" + repository.getName() 
				+ "@checksum=ignore"
				+ (repository.isSnapshotsAllowed()?"@snapshots":"")
			);
		
		// check if 'updates' repository is defined
		repository = artifactService.getRepository(ArtifactRepository.REPO_NAME_UPDATES);
		if (!repository.isHealthy() && repository.isEnabled()) {
			// TODO create own exceptions 
			throw new InitializerException(this
					, String.format("Repository [{}] is not healthy, but enabled", repository.getName())
					, "Please check the configuration", null);
		}
		if (!repository.isEnabled())
			log.warn("Repository [{}] is not enabled, make sure it's not done by mistake", ArtifactRepository.REPO_NAME_UPDATES);
		else
			repositoryUrls.add(
					urlPrefix + repository.getUrl() 
					+ "@id=" + repository.getName()
					+ (repository.isSnapshotsAllowed()?"@snapshots":"")
				);
		
		String repositoryUrlsResult = repositoryUrls.stream().collect(Collectors.joining(", "));
		
		ServiceReference<ConfigRepository> ref1 = this.bundleContext.getServiceReference(ConfigRepository.class);
		this.configRepository = this.bundleContext.getService(ref1);

		/*
		ServiceReference<ConfigurationAdmin> refCa = this.bundleContext.getServiceReference(ConfigurationAdmin.class);
		ConfigurationAdmin ca = this.bundleContext.getService(refCa);

		Dictionary<String, Object> clearProps = new Hashtable<>();
		
		try {
			Configuration config = ca.getConfiguration(MVN_PID);
			clearProps = config.getProperties();
		} catch (IOException e1) {
			log.error("Error getting config: " + MVN_PID, e1);
		}
		*/
	
		try {
			
			TypedProperties properties = this.configRepository.getConfig(MVN_PID);

			String hashKey = PROP_ENTAXY_REPO_LIST + ".md5";
			
			String newHash = DigestUtils.md5Hex(repositoryUrlsResult);
			String currentHash = properties.getOrDefault(hashKey, "").toString();
			
			// To avoid extra config rewriting
			// which may cause config loss while updating
			if (!newHash.equals(currentHash)) {
				properties.put(PROP_ENTAXY_REPO_LIST, repositoryUrlsResult);
				properties.put(hashKey, newHash);
				this.configRepository.update(MVN_PID, properties);
			}
			
		} catch (IOException e) {
			log.error("Error updating config", e);
		} catch (InvalidSyntaxException e) {
			log.error("Error updating config", e);
		}
	}

	@Override
	public void reinit() throws InitializerException {
		// TODO Auto-generated method stub

	}

	protected boolean isHttpEnabled() {
		boolean httpEnabled = true;
		try {
			ConfigRepository configRepository =
					OSGIUtils.services().ofClass(ConfigRepository.class).waitService(20000).get();

			TypedProperties properties = configRepository.getConfig(WEB_CONFIG_PID);

			httpEnabled = Boolean.parseBoolean(properties.getOrDefault(HTTP_ENABLED, "true").toString());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return httpEnabled;
	}

}
