/*-
 * ~~~~~~licensing~~~~~~
 * hazelcast-test
 * ==========
 * 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.platform.core.cluster.persistence.activator;

import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.karaf.cellar.bundle.BundleState;
import org.apache.karaf.cellar.bundle.Constants;
import org.apache.karaf.cellar.bundle.management.CellarBundleMBean;
import org.apache.karaf.cellar.core.ClusterManager;
import org.apache.karaf.cellar.core.GroupManager;
import org.apache.karaf.cellar.core.event.EventHandler;
import org.apache.karaf.cellar.features.FeatureState;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.util.tracker.BaseActivator;
import org.apache.karaf.util.tracker.annotation.ProvideService;
import org.apache.karaf.util.tracker.annotation.RequireService;
import org.apache.karaf.util.tracker.annotation.Services;
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.Member;
import com.hazelcast.core.MemberAttributeEvent;
import com.hazelcast.core.MembershipEvent;
import com.hazelcast.core.MembershipListener;

import ru.entaxy.esb.platform.core.cluster.persistence.PersistenceManager;
import ru.entaxy.esb.platform.core.cluster.persistence.ServiceProvider;
import ru.entaxy.esb.platform.core.cluster.persistence.handler.LocalBundleEventHandler;
import ru.entaxy.esb.platform.core.cluster.persistence.handler.LocalConfigurationEventHandler;
import ru.entaxy.esb.platform.core.cluster.persistence.handler.LocalEventHandlerRegistryDispatcher;
import ru.entaxy.esb.platform.core.cluster.persistence.handler.LocalFeaturesEventHandler;
import ru.entaxy.esb.platform.core.cluster.persistence.handler.LocalHandlerRegistry;
import ru.entaxy.esb.platform.core.cluster.persistence.handler.LocalRepositoryEventHandler;
import ru.entaxy.esb.platform.core.cluster.persistence.handler.LocalTopicConsumer;

@Services(requires = {
    @RequireService(ClusterManager.class),
    @RequireService(GroupManager.class),
    @RequireService(CellarBundleMBean.class),
    @RequireService(ConfigurationAdmin.class),
    @RequireService(FeaturesService.class),
    @RequireService(HazelcastInstance.class)
},
        provides = {
            @ProvideService(EventHandler.class)
        })
public class Activator extends BaseActivator implements ServiceProvider, MembershipListener {

    private static final String CLASSIFIER_BUNDLE = "bundle";

    private static final String CLASSIFIER_CONFIG = "config";

    private static final String CLASSIFIER_REPOSITORY = "repository";

    private static final String CLASSIFIER_FEATURE = "feature";

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

    private LocalBundleEventHandler bundleEventHandler;

    private LocalConfigurationEventHandler configEventHandler;

    private LocalRepositoryEventHandler repositoryEventHandler;

    private LocalFeaturesEventHandler featuresEventHandler;

    private LocalTopicConsumer consumer;

    private LocalEventHandlerRegistryDispatcher dispatcher;

    private LocalHandlerRegistry handlerRegistry;

    private ClusterManager clusterManager;

    private GroupManager groupManager;

    private HazelcastInstance hazelcastInstance;

    private Map<String, PersistenceManager> managers = new ConcurrentHashMap<>();

    @Override
    protected void doStart() throws Exception {
        clusterManager = getTrackedService(ClusterManager.class);
        if (clusterManager == null)
            return;
        groupManager = getTrackedService(GroupManager.class);
        if (groupManager == null)
            return;
        ConfigurationAdmin configurationAdmin = getTrackedService(ConfigurationAdmin.class);
        if (configurationAdmin == null)
            return;
        FeaturesService featuresService = getTrackedService(FeaturesService.class);
        if (featuresService == null)
            return;

        hazelcastInstance = getTrackedService(HazelcastInstance.class);
        if (hazelcastInstance == null)
            return;

        handlerRegistry = new LocalHandlerRegistry<>();

        /*
         * bundle management
         */

        PersistenceManager<BundleState> bundlePersistenceManager =
                new PersistenceManager<>(this, Constants.BUNDLE_MAP, CLASSIFIER_BUNDLE, BundleState.class);
        this.managers.put(CLASSIFIER_BUNDLE, bundlePersistenceManager);

        bundleEventHandler = new LocalBundleEventHandler();
        bundleEventHandler.setConfigurationAdmin(configurationAdmin);
        bundleEventHandler.setClusterManager(clusterManager);
        bundleEventHandler.setGroupManager(groupManager);
        bundleEventHandler.setBundleContext(bundleContext);
        bundleEventHandler.setFeaturesService(featuresService);
        bundleEventHandler.setPersistenceManager(bundlePersistenceManager);
        bundleEventHandler.init();
        handlerRegistry.addHandler(bundleEventHandler);

        log.debug("Cluster bundleEventHandler started");
        /*
         * config management
         */

        PersistenceManager<Properties> configPersistenceManager =
                new PersistenceManager<>(this, org.apache.karaf.cellar.config.Constants.CONFIGURATION_MAP,
                        CLASSIFIER_CONFIG, Properties.class);
        this.managers.put(CLASSIFIER_CONFIG, configPersistenceManager);

        configEventHandler = new LocalConfigurationEventHandler();
        configEventHandler.setConfigurationAdmin(configurationAdmin);
        configEventHandler.setClusterManager(clusterManager);
        configEventHandler.setGroupManager(groupManager);
        configEventHandler.setPersistenceManager(configPersistenceManager);
        configEventHandler.init();
        handlerRegistry.addHandler(configEventHandler);

        log.debug("Cluster configEventHandler started");
        /*
         * feature repository management
         */

        PersistenceManager<String> repositoryPersistenceManager = new PersistenceManager<>(
                this,
                org.apache.karaf.cellar.features.Constants.REPOSITORIES_MAP,
                CLASSIFIER_REPOSITORY,
                String.class);
        this.managers.put(CLASSIFIER_REPOSITORY, repositoryPersistenceManager);

        repositoryEventHandler = new LocalRepositoryEventHandler();
        repositoryEventHandler.setConfigurationAdmin(configurationAdmin);
        repositoryEventHandler.setClusterManager(clusterManager);
        repositoryEventHandler.setGroupManager(groupManager);
        repositoryEventHandler.setFeaturesService(featuresService);
        repositoryEventHandler.setPersistenceManager(repositoryPersistenceManager);
        repositoryEventHandler.init(bundleContext);
        handlerRegistry.addHandler(repositoryEventHandler);

        log.debug("Cluster repositoryEventHandler started");
        /*
         * feature management
         */
        PersistenceManager<FeatureState> featurePersistenceManager = new PersistenceManager<>(
                this,
                org.apache.karaf.cellar.features.Constants.FEATURES_MAP,
                CLASSIFIER_FEATURE,
                FeatureState.class);
        this.managers.put(CLASSIFIER_FEATURE, featurePersistenceManager);

        featuresEventHandler = new LocalFeaturesEventHandler();
        featuresEventHandler.setConfigurationAdmin(configurationAdmin);
        featuresEventHandler.setClusterManager(clusterManager);
        featuresEventHandler.setGroupManager(groupManager);
        featuresEventHandler.setFeaturesService(featuresService);
        featuresEventHandler.setPersistenceManager(featurePersistenceManager);
        featuresEventHandler.init(bundleContext);
        handlerRegistry.addHandler(featuresEventHandler);

        repositoryPersistenceManager.addDependent(featurePersistenceManager);

        log.debug("Cluster featuresEventHandler started");

        /*
         * kar management
         */
        // TODO


        /*
         * common services 
         */

        dispatcher = new LocalEventHandlerRegistryDispatcher<>();
        dispatcher.setHandlerRegistry(handlerRegistry);
        dispatcher.init();

        consumer = new LocalTopicConsumer();
        consumer.setInstance(hazelcastInstance);
        consumer.setDispatcher(dispatcher);
        consumer.setNode(clusterManager.getNode());
        consumer.setConfigurationAdmin(configurationAdmin);
        consumer.init();


        if (iAmAlone()) {
            restoreAll();
        }
        consumer.start();

        log.debug("Cluster consumer started");
    }

    protected void checkStartStopConsumer() {
        if (iAmAlone())
            consumer.start();
        else
            consumer.stop();
    }

    protected void restoreAll() {
        for (PersistenceManager pm : managers.values())
            pm.restore();
    }

    protected void persistAll() {
        for (PersistenceManager pm : managers.values())
            pm.persist();
    }

    protected boolean iAmAlone() {
        if (hazelcastInstance.getCluster().getMembers().size() > 1)
            return false;
        return true;
    }

    @Override
    protected void doStop() {
        if (consumer != null)
            consumer.stop();
        super.doStop();
    }

    @Override
    public ClusterManager getClusterManager() {
        return clusterManager;
    }

    @Override
    public GroupManager getGroupManager() {
        return groupManager;
    }

    @Override
    public void memberAdded(MembershipEvent membershipEvent) {
        Member member = membershipEvent.getMember();
        Member local = hazelcastInstance.getCluster().getLocalMember();
        checkStartStopConsumer();
    }

    @Override
    public void memberAttributeChanged(MemberAttributeEvent event) {
        // TODO Auto-generated method stub
    }

    @Override
    public void memberRemoved(MembershipEvent membershipEvent) {
        Member member = membershipEvent.getMember();
        Member local = hazelcastInstance.getCluster().getLocalMember();
        if (member.equals(local))
            consumer.stop();
        else if (iAmAlone()) {
            // all other nodes gone
            persistAll();
            consumer.start();
        }

    }
}
