package ru.yandex.qe.dispenser.ws.staff;

import java.util.concurrent.TimeUnit;

import com.google.common.base.Stopwatch;
import com.google.common.base.Ticker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.monlib.metrics.histogram.Histograms;
import ru.yandex.monlib.metrics.labels.Labels;
import ru.yandex.monlib.metrics.primitives.Histogram;
import ru.yandex.monlib.metrics.primitives.Rate;
import ru.yandex.monlib.metrics.registry.MetricRegistry;
import ru.yandex.qe.dispenser.solomon.SolomonHolder;

public class StaffSyncTask {

    private static final Logger LOG = LoggerFactory.getLogger(StaffSyncTask.class);

    public static final String SENSOR_PREFIX = "staff_sync_task.";
    public static final String ELAPSED_TIME_SENSOR = SENSOR_PREFIX + "elapsed_time";
    public static final String ERROR_RATE_SENSOR = SENSOR_PREFIX + "error_rate";
    public static final String LAST_START_SENSOR = SENSOR_PREFIX + "time_since_last_start";
    public static final String LAST_SUCCESS_SENSOR = SENSOR_PREFIX + "time_since_last_success_end";

    private final StaffSyncManager staffSyncManager;
    private final Histogram elapsedTime;
    private final Rate errorRate;
    private final Ticker ticker = Ticker.systemTicker();

    private volatile long lastStart;
    private volatile long lastSuccessEnd;

    public StaffSyncTask(final StaffSyncManager staffSyncManager, final SolomonHolder solomonHolder) {
        this.staffSyncManager = staffSyncManager;
        final MetricRegistry rootRegistry = solomonHolder.getRootRegistry();
        this.elapsedTime = rootRegistry.histogramRate(ELAPSED_TIME_SENSOR, Labels.of(), Histograms.exponential(22, 2, 1.0d));
        this.errorRate = rootRegistry.rate(ERROR_RATE_SENSOR, Labels.of());
        this.lastStart = TimeUnit.NANOSECONDS.toMillis(ticker.read());
        rootRegistry.lazyGaugeInt64(LAST_START_SENSOR, Labels.of(), () -> TimeUnit.NANOSECONDS.toMillis(ticker.read()) - lastStart);
        this.lastSuccessEnd = TimeUnit.NANOSECONDS.toMillis(ticker.read());
        rootRegistry.lazyGaugeInt64(LAST_SUCCESS_SENSOR, Labels.of(), () -> TimeUnit.NANOSECONDS.toMillis(ticker.read()) - lastSuccessEnd);
    }

    public void update() {
        LOG.info("Running staff sync task...");
        final Stopwatch stopwatch = Stopwatch.createStarted();
        lastStart = TimeUnit.NANOSECONDS.toMillis(ticker.read());
        boolean success = false;
        try {
            staffSyncManager.syncAll();
            success = true;
        } catch (Throwable e) {
            LOG.error("Staff sync failure", e);
            throw e;
        } finally {
            stopwatch.stop();
            final long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
            if (success) {
                lastSuccessEnd = TimeUnit.NANOSECONDS.toMillis(ticker.read());
                LOG.info("Staff sync finished successfully in {} seconds", TimeUnit.MILLISECONDS.toSeconds(elapsed));
            } else {
                errorRate.inc();
                LOG.info("Staff sync finished with error in {} seconds", TimeUnit.MILLISECONDS.toSeconds(elapsed));
            }
            elapsedTime.record(elapsed);
        }
    }

}
