package com.atlassian.stash.internal.maintenance;

import com.atlassian.event.api.EventPublisher;
import com.atlassian.johnson.event.AddEvent;
import com.atlassian.johnson.event.RemoveEvent;
import com.atlassian.scheduler.SchedulerServiceException;
import com.atlassian.scheduler.core.LifecycleAwareSchedulerService;
import com.atlassian.bitbucket.event.maintenance.MaintenanceEndedEvent;
import com.atlassian.bitbucket.event.maintenance.MaintenanceStartedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;

/**
 * @since 3.0
 */
public class DefaultMaintenanceModeHelper implements MaintenanceModeHelper {

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

    private final EventPublisher eventPublisher;
    private final LifecycleAwareSchedulerService schedulerService;

    public DefaultMaintenanceModeHelper(EventPublisher eventPublisher,
                                        LifecycleAwareSchedulerService schedulerService) {
        this.eventPublisher = eventPublisher;
        this.schedulerService = schedulerService;
    }

    @Override
    public void lock(@Nonnull JohnsonMaintenanceEvent event) {
        pauseScheduler();

        eventPublisher.publish(new MaintenanceStartedEvent(this));
        eventPublisher.publish(new AddEvent(this, event));
    }

    @Override
    public void unlock(@Nonnull JohnsonMaintenanceEvent event) {
        eventPublisher.publish(new RemoveEvent(this, event));
        eventPublisher.publish(new MaintenanceEndedEvent(this));

        resumeScheduler();
    }

    private void pauseScheduler() {
        try {
            //Put the scheduler into standby mode. When the scheduler is restarted, the triggers which should
            //have fired while the scheduler was in standby mode will be fired based on a misfire threshold
            schedulerService.standby();

            //Note: After the scheduler is put into standby, we do _not_ attempt to drain it.
        } catch (SchedulerServiceException e) {
            log.warn("The scheduler threw an exception when it was paused. This usually means the scheduler has " +
                    "already been shutdown.", e);
        }
    }

    private void resumeScheduler() {
        try {
            //Because the scheduler has been in standby mode while the delegate MaintenanceTask ran, it's possible that some jobs
            //may have missed their scheduled start times. Those will trigger a SchedulerException on restart.
            schedulerService.start();
        } catch (SchedulerServiceException e) {
            //Unfortunately for any such jobs, there is very little we can try to do about it. It doesn't make sense to
            //mark the entire migration as failed (because, actually, by this point, we pretty much know it succeeded),
            //so instead we log the exception and move forward.
            log.warn("The scheduler threw an exception when it was resumed. This usually means some jobs missed " +
                    "their configured start time while the scheduler was paused.", e);
        }
    }
}
