/*-
 * ~~~~~~licensing~~~~~~
 * bundle-manager
 * ==========
 * 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.esb.system.management.bundle.manager;

import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.osgi.framework.Constants;

import ru.entaxy.esb.system.management.bundle.jpa.BundleService;
import ru.entaxy.esb.system.management.bundle.jpa.dto.BundleDto;
import ru.entaxy.esb.system.management.bundle.jpa.dto.BundleStatus;
import ru.entaxy.esb.system.management.bundle.jpa.entity.BundleType;
import ru.entaxy.platform.base.support.CommonUtils;
import ru.entaxy.platform.base.support.osgi.bundle.CapabilityDescriptor;
import ru.entaxy.platform.core.artifact.Artifact;
import ru.entaxy.platform.core.artifact.ArtifactManifest;
import ru.entaxy.platform.core.artifact.Artifacts;
import ru.entaxy.platform.core.artifact.DeployedArtifact;
import ru.entaxy.platform.core.artifact.Manifested;
//import ru.entaxy.platform.core.artifact.capabilities.CapabilityDescriptor;
import ru.entaxy.platform.core.artifact.capabilities.ManifestCapabilityHelper;
import ru.entaxy.platform.core.artifact.legacy.BundleController;
import ru.entaxy.platform.core.artifact.service.ArtifactService;
import ru.entaxy.platform.core.blueprint.generator.Blueprint;
import ru.entaxy.platform.core.blueprint.generator.BlueprintGenerator;

public class BundleManagerImpl implements BundleManager {

    private static final String GROUP_ID = "ru.entaxy.esb.";

    private static final String XML_EXTENSION = "xml";

    private BlueprintGenerator blueprintGenerator;
    private BundleController bundleController;
//    private BundleRepository bundleRepository;
    
    private ArtifactService artifactService;

    private BundleService bundleService;

    @Override
    public BundleDto installAndStartBundle(String bundleName, String templateName, String templateFileName, String bundleType, String bundleVersion,
                                           URL templateUrl, Map<String, String> param) throws Exception {
    	return doInstallBundle(bundleName, templateName, templateFileName, bundleType, bundleVersion, templateUrl, param, true);
    }
    
    @Override
    public BundleDto installBundle(String bundleName, String templateName, String templateFileName, String bundleType, String bundleVersion,
                                           URL templateUrl, Map<String, String> param) throws Exception {
    	return doInstallBundle(bundleName, templateName, templateFileName, bundleType, bundleVersion, templateUrl, param, false);
    }

	private BundleDto doInstallBundle(String bundleName, String templateName, String templateFileName, String bundleType, String bundleVersion,
			URL templateUrl, Map<String, String> param, boolean start) throws Exception {
        BundleDto bundle = new BundleDto(templateName + "." + bundleName, BundleType.getValueOf(bundleType.toUpperCase()));
        Blueprint blueprint = blueprintGenerator.createBlueprint(templateUrl, templateFileName, bundleName, param);
        installAndStartBundle(bundleType, bundleVersion, bundle, blueprint, start);
		bundle.setBundleName(getFullBundleNameByNameAndType(templateName + "." + bundleName, bundleType));
        return bundle;
	}

    @Override
    public BundleDto installAndStartBundle(String bundleName, String bundleType, String bundleVersion,
                                           Map<String, String> param) throws Exception {
        return doInstallBundle(bundleName, bundleType, bundleVersion, param, true);
    }
    
    @Override
    public BundleDto installBundle(String bundleName, String bundleType, String bundleVersion,
                                           Map<String, String> param) throws Exception {
        return doInstallBundle(bundleName, bundleType, bundleVersion, param, false);
    }

	private BundleDto doInstallBundle(String bundleName, String bundleType, String bundleVersion,
			Map<String, String> param, boolean start) throws Exception {
		BundleDto bundle = new BundleDto(getFullBundleNameByNameAndType(bundleName, bundleType)
				, BundleType.getValueOf(bundleType.toUpperCase()));
        Blueprint blueprint = blueprintGenerator.createBlueprint(bundleType, bundleName, param);
        installAndStartBundle(bundleType, bundleVersion, bundle, blueprint, start);
        return bundle;
	}

    @Override
    public BundleDto installAndStartBundle(String bundleName, String bundleType, String bundleVersion, Blueprint blueprint) throws Exception {
    	return doInstallBundle(bundleName, bundleType, bundleVersion, blueprint, true);
    }
    
    @Override
    public BundleDto installBundle(String bundleName, String bundleType, String bundleVersion, Blueprint blueprint) throws Exception {
        return doInstallBundle(bundleName, bundleType, bundleVersion, blueprint, false);
    }

	private BundleDto doInstallBundle(String bundleName, String bundleType, String bundleVersion, Blueprint blueprint, boolean start)
			throws Exception {
		BundleDto bundle = new BundleDto(getFullBundleNameByNameAndType(bundleName, bundleType)
				, BundleType.getValueOf(bundleType.toUpperCase()));
        installAndStartBundle(bundleType, bundleVersion, bundle, blueprint, start);
        return bundle;
	}

    @Override
    public BundleDto installAndStartBundle(String bundleName, String bundleType, String bundleVersion) throws Exception {
    	return doInstallBundle(bundleName, bundleType, bundleVersion, true);
    }

    @Override
    public BundleDto installBundle(String bundleName, String bundleType, String bundleVersion) throws Exception {
        return doInstallBundle(bundleName, bundleType, bundleVersion, false);
    }

	private BundleDto doInstallBundle(String bundleName, String bundleType, String bundleVersion, boolean start)
			throws Exception {
		BundleDto bundle = new BundleDto(bundleType + "-" + bundleName
				, BundleType.getValueOf(bundleType.toUpperCase()));
        Blueprint blueprint = blueprintGenerator.createBlueprint(bundleType, bundleName, new HashMap<>());
        installAndStartBundle(bundleType, bundleVersion, bundle, blueprint, start);
        return bundle;
	}

    @Override
	public BundleDto startBundle(BundleDto bundle) throws Exception {
    	bundle.setStatus(BundleStatus.valueOf(
    		CommonUtils.getValid(bundleController.startBundle(bundle.getBundleName()), "Resolved").toUpperCase()
    			));
        return bundle;
	}

	@Override
	public BundleDto stopBundle(BundleDto bundle) throws Exception {
		bundle.setStatus(BundleStatus.valueOf(
			CommonUtils.getValid(bundleController.stopBundle(bundle.getBundleName()),"Resolved").toUpperCase()
			 ));
	    return bundle;
	}

	@Override
	public void startBundle(String bundleName) throws Exception {
		bundleController.startBundle(bundleName);
	}

	@Override
	public void stopBundle(String bundleName) throws Exception {
		bundleController.stopBundle(bundleName);
	}

	@Override
    public void uninstallBundle(String bundleName, String type, String bundleVersion) throws Exception {
    	// TODO decide if really need to delete artifacts
        // bundleRepository.delete(GROUP_ID + type, bundleVersion, type + "-" + bundleName, XML_EXTENSION);
        bundleController.uninstallBundle(getFullBundleNameByNameAndType(bundleName, type));
    }


    @Override
    public void uninstallBundleByName(String bundleName, String type, String bundleVersion) throws Exception {
    	// TODO decide if really need to delete artifacts
        // bundleRepository.delete(GROUP_ID + type, bundleVersion, bundleName, XML_EXTENSION);
        bundleController.uninstallBundle(bundleName);
    }

	@Override
    public void restore(BundleDto bundle) throws Exception {
    	bundleController.installAndStartBundle(bundle.getBundleUrl(), bundle.getBundleName());
        
        checkCurrentBundleStatus(bundle);
    }

	@Override
	public void checkCurrentBundleStatus(BundleDto bundle) throws Exception {
		if (BundleStatus.RESOLVED.equals(bundle.getStatus())) {
            bundleController.stopBundle(bundle.getBundleName());
        } else if (BundleStatus.ACTIVE.equals(bundle.getStatus())) {
            bundleController.startBundle(bundle.getBundleName());
        }
	}

    private void installAndStartBundle(String bundleType, String bundleVersion, BundleDto bundle, Blueprint blueprint, boolean start)
            throws Exception {
    	
    	Artifact artifact = Artifacts.create(Artifact.ARTIFACT_CATEGORY_BLUEPRINT);
    	artifact.getCoordinates()
    		.groupId(Artifact.DEFAULT_RUNTIME_GROUP_ID + "." + bundleType)
    		.artifactId(bundle.getBundleName())
    		.version(bundleVersion);
    	artifact.setContent(blueprint.getBody());
    	
    	// TODO remove hardcode after blueprint-generator is removed
    	// and all the operations are switched to artifact-management
    	if (artifact.isManifested()) {

    		ArtifactManifest artifactManifest = ((Manifested)artifact).getManifest();
    		ManifestCapabilityHelper capabilityHelper = new ManifestCapabilityHelper(artifactManifest);
    		
    		CapabilityDescriptor descriptor = capabilityHelper.provideCapability(artifact.getCoordinates().getGroupId())
    	    		.attribute("name", bundle.getBundleName().replace(bundleType + "-", ""))
    	    		.attribute("bundleName", bundle.getBundleName());

        	if (blueprint.getProperties().containsKey("systemName")) {
        		Object val = blueprint.getProperties().get("systemName");
        		if (val != null)
        			descriptor.attribute("system", val.toString());
        	}
        	if (blueprint.getProperties().containsKey("template.id")) {
        		Object val = blueprint.getProperties().get("template.id");
        		Object val2 = blueprint.getProperties().get("template.name");
        		if (val != null)
        			descriptor.attribute("templateId", val.toString());
        		else 
        			descriptor.attribute("templateId", val2.toString());
        		descriptor.attribute("templateName", val2.toString());
        	}
    		
    		capabilityHelper.save();
    	}
    	
    	DeployedArtifact deployedArtifact = artifactService.deployShared(artifact);
    	bundle.setBundleUrl("blueprint:" + deployedArtifact.getLocation());
    	
    	String finalBundleName = deployedArtifact.getArtifact()
    			.getProperties().getOrDefault(Constants.BUNDLE_NAME
    					, deployedArtifact.getArtifact()
    	    				.getProperties().getOrDefault(Constants.BUNDLE_SYMBOLICNAME, bundle.getBundleName())).toString();
    	bundle.setBundleName(finalBundleName);
    	
//        bundle.setBundleUrl(bundleRepository.deployBlueprint(GROUP_ID + bundleType, bundleVersion, bundle.getBundleName(),
//                XML_EXTENSION, blueprint.getBody()));
        try {
        	if (start) {
        		bundle.setStatus(BundleStatus.valueOf(
                    bundleController.installAndStartBundle(bundle.getBundleUrl(), bundle.getBundleName())));
        	} else {
        		bundleController.installBundle(bundle.getBundleUrl());
        	}
        } catch (Exception ex) {
        	// TODO decide if we really need to delete artifact from repository
//            bundleRepository.delete(GROUP_ID + bundleType, bundleVersion, bundle.getBundleName(),
//                    XML_EXTENSION);
            throw ex;
        }
    }

    protected String getFullBundleNameByNameAndType(String bundleName, String bundleType) {
    	return Artifact.DEFAULT_RUNTIME_GROUP_ID + "." + bundleType + "." + bundleName;
    }
    
    public void setBundleService(BundleService bundleService) {
        this.bundleService = bundleService;
    }

    public void setBlueprintGenerator(BlueprintGenerator blueprintGenerator) {
        this.blueprintGenerator = blueprintGenerator;
    }

    public void setBundleController(BundleController bundleController) {
        this.bundleController = bundleController;
    }

/*    
    public void setBundleRepository(BundleRepository bundleRepository) {
        this.bundleRepository = bundleRepository;
    }
*/

	public void setArtifactService(ArtifactService artifactService) {
		this.artifactService = artifactService;
		this.bundleController = this.artifactService.installers().cluster().bundleController();
	}
    
}
