package ru.yandex.autotests.direct.web.api.core;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.autotests.direct.db.steps.DirectJooqDbSteps;
import ru.yandex.autotests.direct.utils.config.DirectTestRunProperties;
import ru.yandex.autotests.direct.web.api.steps.DirectWebApiSteps;
import ru.yandex.junitextensions.rules.retry.RetryRule;

public class DirectRule implements TestRule {

    private static Integer DEFAULT_RETRY_TIMEOUT = 0;
    public final Logger LOG;
    private List<TestRule> rules = new LinkedList<>();
    private DirectWebApiSteps webApiSteps;
    private Boolean useAuth = true;
    private String authLogin;
    private DirectJooqDbSteps dbSteps;

    protected DirectRule() {
        LOG = LoggerFactory.getLogger(this.getClass());
    }

    public static DirectRule defaultClassRule() {
        return new DirectRule()
                .withRules(defaultClassRuleChain());
    }

    public static DirectRule defaultRule() {
        return new DirectRule().withRules(defaultRuleChain());
    }

    private static RetryRule retryOnError() {
        return RetryRule
                .retry()
                .times(DirectTestRunProperties.getInstance().getDirectCmdRetryTimes())
                .every(DEFAULT_RETRY_TIMEOUT, TimeUnit.SECONDS);
    }

    private static RuleChain defaultClassRuleChain() {
        DirectTestRunProperties p = DirectTestRunProperties.getInstance();
        TestRule semaphoreRule = StageSemaphore.getSemaphore(p.getDirectHost(), p.getDirectSemaphorePermits());

        RuleChain chain = RuleChain.emptyRuleChain();
        if (semaphoreRule != null) {
            chain = chain.around(semaphoreRule);
        }
        return chain;
    }

    private static RuleChain defaultRuleChain() {
        return RuleChain.emptyRuleChain().around(retryOnError());
    }

    private DirectTestRunProperties properties() {
        return DirectTestRunProperties.getInstance();
    }

    public DirectRule withRules(TestRule... rules) {
        Collections.addAll(this.rules, rules);
        return this;
    }

    public DirectRule as(String authLogin) {
        this.authLogin = authLogin;
        return this;
    }

    public String getAuthLogin() {
        if (authLogin == null) {
            authLogin = DirectTestRunProperties.getInstance().getDirectWebSuperLogin();
        }
        return authLogin;
    }

    public DirectWebApiSteps webApiSteps() {
        if (webApiSteps == null) {
            webApiSteps = new DirectWebApiSteps(this.properties());
        }
        return webApiSteps;
    }

    public DirectJooqDbSteps dbSteps() {
        if (dbSteps == null) {
            dbSteps = new DirectJooqDbSteps();
        }
        return dbSteps;
    }

    public DirectRule useAuth(Boolean useAuth) {
        this.useAuth = useAuth;
        return this;
    }

    private RuleChain buildChain() {
        RuleChain chain = RuleChain.emptyRuleChain();
        if (useAuth) {
            chain = chain.around(new AuthRule(getAuthLogin()).withDirectWebApiSteps(webApiSteps()));
        }
        for (TestRule rule : rules) {
            if (rule instanceof NeedsWebApiSteps) {
                ((NeedsWebApiSteps) rule).withDirectWebApiSteps(webApiSteps());
            }
            chain = chain.around(rule);
        }
        return chain;
    }

    public Logger log() {
        return LOG;
    }

    @Override
    public Statement apply(Statement base, Description description) {
        return buildChain().apply(base, description);
    }
}
