/*-
 * ~~~~~~licensing~~~~~~
 * core-support-runtime
 * ==========
 * 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.platform.core.support.runtime.cluster;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock;

import org.apache.camel.CamelContext;
import org.apache.camel.cluster.CamelClusterMember;
import org.apache.camel.cluster.CamelClusterService;
import org.apache.camel.support.cluster.AbstractCamelClusterView;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JGroupsLockClusterView extends AbstractCamelClusterView {

    private static final transient Logger LOG = LoggerFactory.getLogger(JGroupsLockClusterView.class);
    private final CamelClusterMember localMember = new JGroupsLocalMember();
    private String lockName;
    
    private Lock lock;
    private ScheduledExecutorService executor;
    private volatile boolean isMaster;

    protected JGroupsLockClusterView(CamelClusterService cluster, String namespace) {
        super(cluster, namespace);
        lockName = namespace;
    }

    @Override
    public Optional<CamelClusterMember> getLeader() {
        if (isMaster) {
            return Optional.of(localMember);
        } else {
            return Optional.empty();
        }
    }

    @Override
    public CamelClusterMember getLocalMember() {
        return localMember;
    }

    @Override
    public List<CamelClusterMember> getMembers() {
        return new ArrayList<CamelClusterMember>() {{ add(localMember); }};
    }

    @Override
    protected void doStart() throws Exception {
        if (lock != null) {
            lock.unlock();
            lock = null;
        }
        JGroupsLockClusterService service = getClusterService().unwrap(JGroupsLockClusterService.class);
        lock = service.getLockService().getLock(lockName);

        // Camel context should be set at this stage.
        final CamelContext context = ObjectHelper.notNull(getCamelContext(), "CamelContext");
        executor = context.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "JGroupsLockClusterView-" + getClusterService().getId() + "-" + lockName);
        executor.schedule(new Runnable() {
                    @Override
                    public void run() {
                        LOG.info("Attempting to become master acquiring the lock for group: " + lockName + " in JGroups cluster");
                        lock.lock();
                        isMaster = true;
                        fireLeadershipChangedEvent(Optional.ofNullable(localMember));
                        LOG.info("Became master by acquiring the lock for group: " + lockName + " in JGroups cluster");
                    }
                },
                service.getAcquireLockDelay(),
                service.getAcquireLockDelayUnit());
    }

    @Override
    protected void doStop() throws Exception {
        LOG.trace("doStop: " + lockName);
        stopProcess();
    }

    @Override
    protected void doShutdown() throws Exception {
        LOG.trace("doShutdown: " + lockName);
        stopProcess();
    }
    
    protected void stopProcess() throws Exception {
        shutdownExecutor();
        fireLeadershipChangedEvent(Optional.empty());
        clearLock();
        isMaster = false;
    }

    private void clearLock() throws Exception {
        if (lock != null && isMaster) {
            LOG.trace("tryUnlock: " + lockName);
            JGroupsLockClusterService service = getClusterService().unwrap(JGroupsLockClusterService.class);
            service.getLockService().unlockForce(lockName);
            LOG.debug("unlocked: " + lockName);
            lock = null;
        }
    }

    private void shutdownExecutor() {
        CamelContext context = getCamelContext();
        if (executor != null) {
            if (context != null) {
                context.getExecutorServiceManager().shutdown(executor);
            } else {
                executor.shutdown();
            }
            executor = null;
        }
    }

    private final class JGroupsLocalMember implements CamelClusterMember {
        @Override
        public boolean isLeader() {
            return isMaster;
        }

        @Override
        public boolean isLocal() {
            return true;
        }

        @Override
        public String getId() {
            return getClusterService().getId();
        }
    }
}
