package ru.yandex.wmtools.common.service;


import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.wmtools.common.error.ExtraTagInfo;
import ru.yandex.wmtools.common.error.ExtraTagNameEnum;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.InternalProblem;
import ru.yandex.wmtools.common.servantlet.AbstractServantlet;

/**
* @author baton
*/
public class DBBanner {
    private static final Logger log = LoggerFactory.getLogger(DBBanner.class);

    private final int errorsToBan; // after how many query attempts with error master-base should be banned.
    private final int banAttemptsCount; // how many times queries will be rejected.
    private final int banTimeInMilliseconds; // ban time in milliseconds
    private final ExtraTagInfo databaseInfoExtraTag; // additional database info - to output to log

    private volatile int lastErrors = 0; // how many last query attempts caused errors?
    private volatile int bannedRemain = 0; // how many attempts remaining should be rejected by master-base
    private volatile Date banStart = new Date(); // when DB ban was started

    public DBBanner(int errorsToBan, int banAttemptsCount, int banTimeInMilliseconds, String databaseInfo) {
        this.errorsToBan = errorsToBan;
        this.banAttemptsCount = banAttemptsCount;
        this.banTimeInMilliseconds = banTimeInMilliseconds;
        this.databaseInfoExtraTag = new ExtraTagInfo(ExtraTagNameEnum.DB_INFO, databaseInfo);

        log.info("DBBanner created with parameters: [" + errorsToBan + ", " + banAttemptsCount + ", " + banTimeInMilliseconds + "]");
    }

    /**
     * Called before query to master. This method must decide, do we use VETO to query master-base or not.
     * @throws InternalException Thrown if VETO is used.
     */
    public synchronized void internalQueryVeto() throws InternalException {
        if (isBanned()) {
            bannedRemain--;
            throw new InternalException(InternalProblem.MASTER_DB_BANNED, AbstractServantlet.getDatabaseInfoLine(databaseInfoExtraTag) + "Master DB is BANNED!", databaseInfoExtraTag);
        }
    }

    public synchronized boolean isBanned() {
        if (System.currentTimeMillis() - banStart.getTime() > banTimeInMilliseconds) {
            bannedRemain = 0;
        }

        return bannedRemain > 0;
    }

    /**
     * Called when query was not sent to master-base because of error.
     */
    public synchronized void internalQueryFailed() {
        lastErrors++;
        if (lastErrors >= errorsToBan) {
            lastErrors = 0;
            bannedRemain = banAttemptsCount;
            banStart = new Date();
        }
    }

    /**
     * Called to indicate that no error was found while trying to query master-base.
     * Method can be called only when it's no ban.
     */
    public synchronized void internalAllRight() {
        lastErrors = 0;
    }
}
