/*-
 * ~~~~~~licensing~~~~~~
 * connector-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.connector.manager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import ru.entaxy.esb.system.common.exception.TemplateNotFound;
import ru.entaxy.esb.system.connector.entity.Connector;
import ru.entaxy.esb.system.connector.impl.ConnectorService;
import ru.entaxy.esb.system.core.template.Template;
import ru.entaxy.esb.system.core.template.TemplateCollector;
import ru.entaxy.esb.system.jpa.SystemService;
import ru.entaxy.esb.system.jpa.entity.System;
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.BundleEntity;
import ru.entaxy.esb.system.management.bundle.manager.BundleManager;
import ru.entaxy.esb.system.management.connector.manager.dto.ConnectorDto;
import ru.entaxy.esb.system.management.connector.manager.mapper.ConnectorMapper;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ConnectorManagerImpl implements ConnectorManager {

	private static final Log LOG = LogFactory.getLog(ConnectorManagerImpl.class);
	
    private static final String CONNECTOR = "connector";

    private boolean isRestoreFromDB;

    private BundleManager bundleManager;
    private TemplateCollector templateCollector;
    private SystemService systemService;
    private ConnectorService connectorService;
    private ConnectorMapper connectorMapper;

    @Override
    public BundleDto installConnector(ConnectorDto connectorDto)
            throws Exception {
        URL templateUrl = getTemplateLocation(connectorDto.getTemplateName());
        System system = systemService.getByUuid(connectorDto.getSystemUuid());
        Connector connector = connectorService.addConnector(system, connectorMapper.toConnectorEntity(connectorDto));
        Template template = getTemplate(connectorDto.getTemplateName());
        BundleDto bundle;
        try {
            bundle = bundleManager.installAndStartBundle(system.getName(), template.getTemplateName(), template.getTemplateFileName(),
                    CONNECTOR, String.valueOf(connector.getVersion()), templateUrl,
                    mergeParams(connectorDto.getTemplateName(), connectorDto.getConnectorParams()));
            connectorService.setBundleEntity(connector, connectorMapper.toBundleEntity(bundle));
        } catch (Exception ex) {
            connectorService.removeConnector(connector);
            throw ex;
        }
        return bundle;
    }

    @Override
    public BundleDto startConnector(ConnectorDto connectorDto) throws Exception {
        Connector connector = connectorService.getConnectorByName(connectorDto.getSystemUuid(), connectorDto.getTemplateName());
        BundleDto bundle = bundleManager.startBundle(connectorMapper.toBundleDto(connector.getBundleEntity()));
        connectorService.setBundleEntity(connector, connectorMapper.toBundleEntity(bundle));
        return bundle;
    }

    @Override
    public BundleDto startConnector(Connector connector) throws Exception {
        BundleDto bundle = bundleManager.startBundle(connectorMapper.toBundleDto(connector.getBundleEntity()));
        connectorService.setBundleEntity(connector, connectorMapper.toBundleEntity(bundle));
        return bundle;
    }
    
    @Override
    public BundleDto stopConnector(ConnectorDto connectorDto) throws Exception {
        Connector connector = connectorService.getConnectorByName(connectorDto.getSystemUuid(), connectorDto.getTemplateName());
        BundleDto bundle = bundleManager.stopBundle(connectorMapper.toBundleDto(connector.getBundleEntity()));
        connectorService.setBundleEntity(connector, connectorMapper.toBundleEntity(bundle));
        return bundle;
    }

    @Override
    public BundleDto stopConnector(Connector connector) throws Exception {
        BundleDto bundle = bundleManager.stopBundle(connectorMapper.toBundleDto(connector.getBundleEntity()));
        connectorService.setBundleEntity(connector, connectorMapper.toBundleEntity(bundle));
        return bundle;
    }

    @Override
    public BundleDto uninstallConnector(ConnectorDto connectorDto) throws Exception {
        Connector connector = connectorService.getConnectorByName(connectorDto.getSystemUuid(), connectorDto.getTemplateName());
        BundleEntity bundleEntity = connector.getBundleEntity();
        bundleManager.uninstallBundleByName(bundleEntity.getName(), CONNECTOR, String.valueOf(connector.getVersion()));
        connectorService.removeConnector(connector);
        return new BundleDto(connector.getName(), BundleStatus.UNINSTALL);
    }

    @Override
    public BundleDto uninstallConnector(Connector connector) throws Exception {
        BundleEntity bundleEntity = connector.getBundleEntity();
        bundleManager.uninstallBundleByName(bundleEntity.getName(), CONNECTOR, String.valueOf(connector.getVersion()));
        connectorService.removeConnector(connector);        
        return new BundleDto(connector.getName(), BundleStatus.UNINSTALL);
    }

    @Override
    public List<ConnectorDto> getListConnector() {
        return connectorMapper.toConnectorDtos(connectorService.getListConnector());
    }

    @Override
    public List<Template> getTemplates() {
        List<Template> templateList = new ArrayList<>();
        for (String referenceName : templateCollector.getReferenceNames()) {
            templateList.add(templateCollector.getReference(referenceName));
        }
        return templateList;
    }

    @Override
    public Template getTemplate(String templateName) {
        return templateCollector.getReference(templateName);
    }

    private Map<String, String> mergeParams(String templateName, Map<String, String> params) {
        if (params == null) {
            params = new HashMap<>();
        }
        if (templateCollector.getReference(templateName).getParams() == null) {
            return params;
        }
        for (Map.Entry<String, String> entry : templateCollector.getReference(templateName).getParams().entrySet()) {
            if (!params.containsKey(entry.getKey())) {
                params.put(entry.getKey(), entry.getValue());
            }
        }
        return params;
    }

    private URL getTemplateLocation(String templateName) throws TemplateNotFound {
        try {
            return templateCollector.getReference(templateName).getTemplateLocation();
        } catch (NullPointerException e) {
            throw new TemplateNotFound("Template " + templateName + " not found", e);
        }
    }
    
    @Override
	public void restore() throws Exception {
        if (!isRestoreFromDB)
            return;
    	for (Connector connector : connectorService.getListConnector()) {
			try {
				LOG.info("Restore connector " + connector.getName());
				bundleManager.restore(connectorMapper.toBundleDto(connector.getBundleEntity()));
			} catch (IOException e) {
				LOG.warn("Recreate connector " + connector.getName());
				restoreConnector(connector);
			}
		}
	}

	private void restoreConnector(Connector connector) throws Exception  {
		URL templateUrl = getTemplateLocation(connector.getTemplateName());
        System system =  systemService.get(connector.getSystemId());
        Template template = getTemplate(connector.getTemplateName());

        BundleDto bundle = bundleManager.installBundle(system.getName(), template.getTemplateName(), template.getTemplateFileName(),
                CONNECTOR, String.valueOf(connector.getVersion()), templateUrl,
                mergeParams(connector.getTemplateName(), connectorMapper.toConnectorParams(connector.getConnectorParams())));
		
        bundle.setStatus(connectorMapper.toBundleDto(connector.getBundleEntity()).getStatus());
        
		bundleManager.checkCurrentBundleStatus(connectorMapper.toBundleDto(connector.getBundleEntity()));
		
		connectorService.setBundleEntity(connector, connectorMapper.toBundleEntity(bundle));
	}

	public void setBundleManager(BundleManager bundleManager) {
        this.bundleManager = bundleManager;
    }

    public void setTemplateCollector(TemplateCollector templateCollector) {
        this.templateCollector = templateCollector;
    }

    public void setSystemService(SystemService systemService) {
        this.systemService = systemService;
    }

    public void setConnectorService(ConnectorService connectorService) {
        this.connectorService = connectorService;
    }

    public void setConnectorMapper(ConnectorMapper connectorMapper) {
        this.connectorMapper = connectorMapper;
    }

    public void setIsRestoreFromDB(String isRestoreFromDB) {
        this.isRestoreFromDB = Boolean.parseBoolean(isRestoreFromDB);
    }
}
