package ru.yandex.webmaster3.storage.user.service;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.google.common.base.Strings;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.storage.abt.AbtService;
import ru.yandex.webmaster3.storage.user.CustomizableSurveyData;
import ru.yandex.webmaster3.storage.user.CustomizableSurveyTypeEnum;
import ru.yandex.webmaster3.storage.user.dao.CustomizableSurveyYDao;
import ru.yandex.webmaster3.storage.util.ydb.exception.WebmasterYdbException;

@Service("customizableSurveyService")
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class CustomizableSurveyService {
    private static final long CACHE_DURATION_MINUTES = 30;
    private static final long REFRESH_DURATION_MINUTES = 2;
    private static final Object KEY = new Object();

    private final AbtService abtService;
    private final CustomizableSurveyYDao customizableSurveyYDao;

    private final SurveyStateService surveyStateForUserService;

    private final ExecutorService executor = Executors.newSingleThreadExecutor();

    private LoadingCache<Object, Set<CustomizableSurveyData>> cache = CacheBuilder.newBuilder()
            .expireAfterAccess(CACHE_DURATION_MINUTES, TimeUnit.MINUTES)
            .refreshAfterWrite(REFRESH_DURATION_MINUTES, TimeUnit.MINUTES)
            .build(new CacheLoader<>() {
                @Override
                public Set<CustomizableSurveyData> load(@NotNull Object key) {
                    return new HashSet<>(customizableSurveyYDao.getServiceSurveys());
                }

                @Override
                public ListenableFuture<Set<CustomizableSurveyData>> reload(@NonNull Object key,
                                                                            Set<CustomizableSurveyData> oldValue) {
                    final var task = ListenableFutureTask.create(() -> {
                                try {
                                    return new HashSet<>(customizableSurveyYDao.getServiceSurveys());
                                } catch (WebmasterYdbException e) {
                                    return oldValue;
                                }
                            }
                    );
                    executor.submit(task);
                    return task;
                }
            });

    @NonNull
    public Set<CustomizableSurveyData> getServiceSurveys(String routeName,
                                                         boolean onlyEnabled,
                                                         WebmasterHostId hostId,
                                                         long userId) {
        return cache.getUnchecked(KEY).stream()
                .filter(survey -> !onlyEnabled || survey.isEnabled())
                .filter(survey -> routeName.matches(survey.getRouteNameRegexp()))
                .filter(survey -> Strings.isNullOrEmpty(survey.getExperiment()) || // либо нет экспериментов
                        (hostId != null && abtService.isInExperiment(hostId, survey.getExperiment())) ||
                        (abtService.isInExperiment(userId, survey.getExperiment()))
                )
                .filter(survey -> !survey.isSurveyByHost() || hostId != null)
                .filter(survey -> !surveyStateForUserService.isClosed(survey.getSurveyId(), userId,
                        survey.getDaysBeforeShowAgain(), hostId, survey.isSurveyByHost()))
                .collect(Collectors.toSet());
    }

    public CustomizableSurveyTypeEnum getServiceSurveyAnswerPlace(String surveyId) {
        return cache.getUnchecked(KEY).stream()
                .filter(survey -> Objects.equals(survey.getSurveyId(), surveyId))
                .findAny()
                .map(CustomizableSurveyData::getSurveyType).orElse(null);
    }
}
