package ru.yandex.webmaster3.internal.http;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.joda.time.Duration;

import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.ActionRequest;
import ru.yandex.webmaster3.core.http.ActionResponse;
import ru.yandex.webmaster3.core.http.ActionRouter;
import ru.yandex.webmaster3.core.http.WebmasterCommonErrorSystem;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.solomon.metric.SolomonKey;
import ru.yandex.webmaster3.core.solomon.metric.SolomonTimer;
import ru.yandex.webmaster3.internal.common.InternalRequest;
import ru.yandex.webmaster3.internal.common.security.InternalClient;
import ru.yandex.webmaster3.internal.common.security.dao.InternalApiClientsYDao;
import ru.yandex.webmaster3.storage.util.ydb.exception.WebmasterYdbException;

/**
 * Специальный ActionRouter, умеюищй отсылать инфу о клиентах и дополнительных ошибках
 * Created by Oleg Bazdyrev on 29/03/2018.
 */
@RequiredArgsConstructor
public class InternalActionRouter extends ActionRouter {

    public static final String CATEGORY = "internal";
    public static final String SECURITY_PREFIX = "SECURITY_";
    public static final String UNKNOWN_CLIENT = "unknown";

    private final InternalApiClientsYDao internalApiClientsYDao;

    private Map<String, InternalActionMetrics> metricsMap;

    @Override
    public void init() {
        initInternalClients();
        super.init();
    }

    private void initInternalClients() {
        try {
            List<InternalClient> clients = internalApiClientsYDao.getAllInternalClients();
            metricsMap = new HashMap<>(clients.size());
            for (InternalClient client : clients) {
                String name = ObjectUtils.firstNonNull(client.getShortName(), client.getName());
                InternalActionMetrics metrics = new InternalActionMetrics(name, CATEGORY);
                metricsMap.put(name, metrics);
            }
            // unknown client
            metricsMap.put(UNKNOWN_CLIENT, new InternalActionMetrics(UNKNOWN_CLIENT, CATEGORY));
        } catch (WebmasterYdbException e) {
            throw new WebmasterException("Could not read internal clients from Cassandra",
                    new WebmasterErrorResponse.YDBErrorResponse(getClass(), e), e);
        }
    }

    @Override
    protected void updateMetrics(ActionRequest actualRequest, String actionName,
                                 ActionResponse.ErrorResponse actionError, long durationMillis) {
        if (actualRequest instanceof InternalRequest) {
            InternalRequest internalRequest = (InternalRequest) actualRequest;
            // save client and security error
            InternalClient client = internalRequest.getInternalClient();
            String clientName = client == null ? UNKNOWN_CLIENT :
                    ObjectUtils.firstNonNull(client.getShortName(), client.getName());
            InternalActionMetrics metrics = metricsMap.get(clientName);
            if (metrics == null) {
                // возможно, в базу добавился новый клиент
                initInternalClients();
                metrics = metricsMap.get(clientName);
            }

            Duration duration = Duration.millis(durationMillis);
            if (actionError == null) {
                metrics.success.update(duration);
            } else if (actionError.getCode().name().startsWith(SECURITY_PREFIX)) {
                metrics.securityError.update(duration);
            } else if (actionError.getSubsystem() == WebmasterCommonErrorSystem.INTERNAL) {
                metrics.internalError.update(duration);
            } else {
                metrics.userError.update(duration);
            }
        }
        super.updateMetrics(actualRequest, actionName, actionError, durationMillis);
    }

    protected class InternalActionMetrics extends ActionMetrics {
        public final SolomonTimer securityError;

        InternalActionMetrics(String client, String category) {
            super(client, category);
            SolomonKey baseKey = createBaseKey(client, category);

            this.securityError = solomonMetricRegistry.createTimer(
                    solomonTimerConfiguration,
                    baseKey.withLabel(SOLOMON_LABEL_RESULT, "security_error")
            );
        }

        @NotNull
        @Override
        protected SolomonKey createBaseKey(String client, String category) {
            return SolomonKey.create("client", client)
                    .withLabel(SOLOMON_LABEL_CATEGORY, category == null ? "<unknown>" : category);
        }
    }
}
