package ee.telekom.workflow.core.lock;

import java.lang.invoke.MethodHandles;
import java.util.Date;

import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ee.telekom.workflow.core.common.WorkflowEngineConfiguration;

/**
 * Manages an exclusive distributed lock per cluster to avoid multiple master.
 */
@Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class LockServiceImpl implements LockService{

    private static final Logger log = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );

    @Autowired
    private LockDao dao;
    @Autowired
    private WorkflowEngineConfiguration config;

    @Override
    public boolean eagerAcquire(){
        releaseExpiredLock();
        return refreshOwnLock() || acquireLock();
    }

    @Override
    public boolean acquireLock(){
        String clusterName = config.getClusterName();
        String nodeName = config.getNodeName();
        Date expireTime = DateUtils.addSeconds( new Date(), config.getHeartbeatMaximumPauseSeconds() );
        boolean isAcquired = dao.create( clusterName, nodeName, expireTime );
        return isAcquired;
    }

    @Override
    public boolean isOwnLock(){
        String nodeName = config.getNodeName();
        return nodeName.equals( getLockOwner() );
    }

    @Override
    public boolean refreshOwnLock(){
        String clusterName = config.getClusterName();
        String nodeName = config.getNodeName();
        Date expireTime = DateUtils.addSeconds( new Date(), config.getHeartbeatMaximumPauseSeconds() );
        boolean isRefreshed = dao.updateExpireTime( clusterName, nodeName, expireTime );
        return isRefreshed;
    }

    @Override
    public boolean releaseOwnLock(){
        String clusterName = config.getClusterName();
        String nodeName = config.getNodeName();
        boolean isReleased = dao.deleteByOwner( clusterName, nodeName );
        if( isReleased ){
            log.info( "Released own lock" );
        }
        else{
            log.error( "Could not release own lock since lock was not owned" );
        }
        return isReleased;
    }

    @Override
    public boolean releaseExpiredLock(){
        String clusterName = config.getClusterName();
        Date now = new Date();
        boolean isReleased = dao.deleteByExpireTime( clusterName, now );
        if( isReleased ){
            log.warn( "Released exprired lock" );
        }
        return isReleased;
    }

    @Override
    public String getLockOwner(){
        return dao.findOwner( config.getClusterName() );
    }

    @Override
    public Date getLockExpireDate(){
        return dao.findExpireTime( config.getClusterName() );
    }

}