/*-
 * ~~~~~~licensing~~~~~~
 * cluster-persistence-service
 * ==========
 * 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;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.stream.Collectors;

import com.hazelcast.core.HazelcastInstanceNotActiveException;
import org.apache.karaf.cellar.core.ClusterManager;
import org.apache.karaf.cellar.core.Configurations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Helper<T> {

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

    public static final String DELIMITER = "~~_DEL_~~";
    public static final String SEPARATOR = "~~_SEP_~~";

    protected String mapBaseName;
    protected String classifier;
    protected Class<T> dataClass;

    protected Serializer<T> serializer;

    protected String rootPath;
    protected String subPath;

    protected ServiceProvider serviceProvider;

    public Helper(ServiceProvider serviceProvider, String mapBaseName, String classifier, Class<T> dataClass) {
        this.serviceProvider = serviceProvider;
        this.mapBaseName = mapBaseName;
        this.classifier = classifier;
        this.dataClass = dataClass;
        this.serializer = new Serializer<>(this.dataClass);
        this.rootPath = System.getProperty("karaf.home");
        this.subPath = "data/cluster-state";
    }

    protected Path getTargetPath(String groupName) {
        String fileName = "cluster." + classifier + "." + groupName + ".state";
        Path p = Paths.get(this.rootPath, this.subPath, fileName);
        return p;
    }

    public void saveClusterState(String groupName) {

        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(this.dataClass.getClassLoader());

        if (!checkGroup(groupName))
            return;

        ClusterManager clusterManager = this.serviceProvider.getClusterManager();

        try {
            Map<String, T> clusterData = clusterManager.getMap(this.mapBaseName + Configurations.SEPARATOR + groupName);

            String serialized = "";
            serialized = clusterData.entrySet().stream()
                    .<String>map((entry) -> {
                        return entry.getKey() + DELIMITER + serializer.toString(entry.getValue());
                    })
                    .collect(Collectors.joining(SEPARATOR));
            Path p = getTargetPath(groupName);
            try {
                //			if (!p.toFile().exists())
                //				p.toFile().createNewFile();
                Files.createDirectories(p.getParent());
                Files.write(p, serialized.getBytes());
            } catch (IOException e) {
                log.error("FAILED saving state to [" + p.toUri().toString() + "]", e);
            }
        } catch (HazelcastInstanceNotActiveException e) {
            log.error("Hazelcast instance is not active for group [" + groupName + "]", e);
        }

        Thread.currentThread().setContextClassLoader(originalClassLoader);

    }

    public void loadClusterState(String groupName) {

        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(this.dataClass.getClassLoader());

        if (!checkGroup(groupName))
            return;

        Path p = getTargetPath(groupName);
        if (!p.toFile().exists()) {
            log.info("File [{}] doesn't exist, skip loading [{}] from [{}]"
                    , p.toUri().toString()
                    , this.classifier
                    , this.mapBaseName);
            return;
        }

        String data = "";
        try {
            data = Files.readString(p);
        } catch (IOException e) {
            log.error("Error reading data from [" + p.toUri().toString() + "]", e);
        }
        if ((data == null) || data.length() == 0)
            return;

        ClusterManager clusterManager = this.serviceProvider.getClusterManager();

        @SuppressWarnings("unchecked")
        Map<String, T> clusterData = clusterManager.getMap(this.mapBaseName + Configurations.SEPARATOR + groupName);

        String[] entries = data.split(SEPARATOR);
        for (int i = 0; i < entries.length; i++) {
            String[] entry = entries[i].split(DELIMITER);
            T object = serializer.fromString(entry[1]);
            clusterData.put(entry[0], object);
        }

        Thread.currentThread().setContextClassLoader(originalClassLoader);

    }

    protected boolean checkGroup(String groupName) {
        // TODO implement check
        return true;
    }

}
