/*-
 * ~~~~~~licensing~~~~~~
 * bundle-manager
 * ==========
 * Copyright (C) 2020 - 2021 EmDev LLC
 * ==========
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.esb.system.management.bundle.manager;

import ru.entaxy.esb.system.deployer.api.BundleRepository;
import ru.entaxy.esb.system.deployer.cellar.deployer.BundleController;
import ru.entaxy.esb.system.management.blueprint.generator.Blueprint;
import ru.entaxy.esb.system.management.blueprint.generator.BlueprintGenerator;
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 java.net.URL;
import java.util.HashMap;
import java.util.Map;

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 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);
        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(bundleType + "-" + bundleName, 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(bundleType + "-" + bundleName, 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(bundleController.startBundle(bundle.getBundleName())));
        return bundle;
	}

	@Override
	public BundleDto stopBundle(BundleDto bundle) throws Exception {
		 bundle.setStatus(BundleStatus.valueOf(bundleController.stopBundle(bundle.getBundleName())));
	     return bundle;
	}

    @Override
    public void uninstallBundle(String bundleName, String type, String bundleVersion) throws Exception {
        bundleRepository.delete(GROUP_ID + type, bundleVersion, type + "-" + bundleName, XML_EXTENSION);
        bundleController.uninstallBundle(type + "-" + bundleName);
    }


    @Override
    public void uninstallBundleByName(String bundleName, String type, String bundleVersion) throws Exception {
        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 {
        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) {
            bundleRepository.delete(GROUP_ID + bundleType, bundleVersion, bundle.getBundleName(),
                    XML_EXTENSION);
            throw ex;
        }
    }

    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;
    }

}
