package ru.yandex.webmaster3.viewer.http;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.Action;
import ru.yandex.webmaster3.core.http.ActionRequest;
import ru.yandex.webmaster3.core.http.ActionResponse;
import ru.yandex.webmaster3.storage.util.ydb.exception.WebmasterYdbException;
import ru.yandex.webmaster3.storage.cache.DashboardCacheService;
import ru.yandex.webmaster3.storage.cache.DashboardType;
import ru.yandex.webmaster3.core.http.request.VerifiedHostIdAware;

import java.io.IOException;
import java.util.Optional;

/**
 * @author avhaliullin
 */
public abstract class AbstractDashboardAction<Req extends ActionRequest & VerifiedHostIdAware, Res extends ActionResponse, Delegate extends Action<Req, Res>> extends Action<Req, Res> {
    private static final Logger log = LoggerFactory.getLogger(AbstractDashboardAction.class);

    private final DashboardType dashboardType;

    protected Delegate delegateAction;
    private DashboardCacheService dashboardCacheService;

    protected AbstractDashboardAction(DashboardType dashboardType) {
        this.dashboardType = dashboardType;
    }

    @Override
    public Res process(Req request) throws WebmasterException {
        String cacheKey = buildCacheKey(request);
        try {
            Optional<Res> cachedResultOpt = dashboardCacheService.getData(request.getHostId(), dashboardType, cacheKey, this::deserializeResponse);
            if (cachedResultOpt.isPresent()) {
                return cachedResultOpt.get();
            }
        } catch (Exception e) {
            log.error("Failed to get value from cache for host " + request.getHostId() +
                    " type " + dashboardType + " cache key " + cacheKey, e);
        }
        prepareRequest(request);
        Res result = delegateAction.process(request);
        try {
            dashboardCacheService.saveData(request.getHostId(), dashboardType, cacheKey, serializeResponse(result));
        } catch (WebmasterYdbException e) {
            log.error("Failed to store cache value from cache for host " + request.getHostId() +
                    " type " + dashboardType + " cache key " + cacheKey, e);
        }
        return result;
    }

    protected abstract byte[] serializeResponse(Res response);

    protected abstract Optional<Res> deserializeResponse(byte[] data) throws IOException;

    protected abstract String buildCacheKey(Req request);

    protected void prepareRequest(Req request) {

    }

    @Required
    public void setDelegateAction(Delegate delegateAction) {
        this.delegateAction = delegateAction;
    }

    @Required
    public void setDashboardCacheService(DashboardCacheService dashboardCacheService) {
        this.dashboardCacheService = dashboardCacheService;
    }
}
