package ru.yandex.webmaster3.viewer.http.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.autodoc.common.doc.params.ParamDescriptor;
import ru.yandex.autodoc.common.doc.params.ParamType;
import ru.yandex.webmaster3.core.http.Action;
import ru.yandex.webmaster3.core.http.ActionEffect;
import ru.yandex.webmaster3.core.http.ActionRequest;
import ru.yandex.webmaster3.core.http.RequestFilter;
import ru.yandex.webmaster3.core.http.internal.ActionReflectionUtils;
import ru.yandex.webmaster3.core.http.request.RequestContext;
import ru.yandex.webmaster3.core.spam.ShowCaptchaReason;
import ru.yandex.webmaster3.viewer.http.spam.CaptchaMetricsService;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author avhaliullin
 */
public abstract class AbstractSpamCaptchaFilter<Req extends ActionRequest> implements RequestFilter<Req, SpamCaptchaFilterResponse> {
    private static final Logger log = LoggerFactory.getLogger(AbstractSpamCaptchaFilter.class);

    private static final String SOLVED_CAPTCHA_PARAM = "solvedCaptcha";

    private static final String TEST_CAPTCHA_PARAM = "balancerParentRequestId";
    private static final String TEST_CAPTCHA_VALUE_SUBSTRING = "Капчу!";

    private CaptchaMetricsService captchaMetricsService;

    @Override
    public boolean isApplicable(Action action) {
        // только для пишущих ручек - по договоренностям с фронтом
        return ActionReflectionUtils.getActionEffect(action.getClass()) == ActionEffect.WRITE;
    }

    @Override
    public SpamCaptchaFilterResponse beforeRequest(RequestContext ctx, Req req) {
        try {
            boolean solvedCaptcha = "true".equals(ctx.getHttpRequest().getParameter(SOLVED_CAPTCHA_PARAM));

            if (solvedCaptcha) {
                captchaMetricsService.captchaSolved(ctx, req);
                onCaptchaSolved(ctx, req);
            } else {
                ShowCaptchaReason reason = null;
                if (captchaTestRequested(ctx)) {
                    reason = ShowCaptchaReason.CAPTCHA_TEST_REQUESTED;
                }
                if (reason == null) {
                    reason = shouldShowCaptcha(ctx, req);
                }
                if (reason != null) {
                    captchaMetricsService.captchaShown(ctx, req, reason);
                    return new SpamCaptchaFilterResponse.ShowCaptcha(getClass(), "Are you robot?", reason);
                }
            }
            return null;
        } catch (Exception e) {
            log.error("Spam filter failed", e);
            return null;
        }
    }

    private boolean captchaTestRequested(RequestContext ctx) {
        String paramVal = ctx.getHttpRequest().getParameter(TEST_CAPTCHA_PARAM);
        return paramVal != null && paramVal.contains(TEST_CAPTCHA_VALUE_SUBSTRING);
    }

    protected abstract ShowCaptchaReason shouldShowCaptcha(RequestContext ctx, Req req) throws Exception;

    protected void onCaptchaSolved(RequestContext ctx, Req req) throws Exception {}

    @Override
    public List<ParamDescriptor> clarifyParameters(List<ParamDescriptor> params) {
        List<ParamDescriptor> result = new ArrayList<>(params.size() + 1);
        result.addAll(params);
        result.add(new ParamDescriptor(SOLVED_CAPTCHA_PARAM, false, ParamType.BOOLEAN, "false",
                "передать true, если пользователь успешно решил капчу", Collections.emptyList()));
        return result;
    }

    @Required
    public void setCaptchaMetricsService(CaptchaMetricsService captchaMetricsService) {
        this.captchaMetricsService = captchaMetricsService;
    }
}
