package ru.yandex.webmaster3.worker.notifications.services;

import org.joda.time.Duration;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.webmaster3.core.blackbox.UserWithLogin;
import ru.yandex.webmaster3.core.util.RetryUtils;
import ru.yandex.webmaster3.core.util.concurrent.graph.BlockingBatchConsumer;
import ru.yandex.webmaster3.core.util.concurrent.graph.GraphOutQueue;
import ru.yandex.webmaster3.storage.user.UserPersonalInfo;
import ru.yandex.webmaster3.storage.user.service.UserPersonalInfoService;
import ru.yandex.webmaster3.worker.notifications.info.UserHostsChannelsInfo;
import ru.yandex.webmaster3.worker.notifications.info.UserHostsPersonalsInfo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by ifilippov5 on 29.08.17.
 */
public class PersonalsResolver {
    private static final RetryUtils.RetryPolicy RETRY_POLICY = RetryUtils.linearBackoff(10, Duration.standardMinutes(2));

    private UserPersonalInfoService userPersonalInfoService;

    public BlockingBatchConsumer<UserHostsChannelsInfo> resolvePersonals(GraphOutQueue<UserHostsPersonalsInfo> out, GraphOutQueue<UserHostsChannelsInfo> blackboxQ) {
        return batch -> {
            UserPersonalInfoService.CachedResponse personals = RetryUtils.query(RETRY_POLICY,
                    () -> userPersonalInfoService.getCachedUsersPersonalInfos(batch.stream().map(UserHostsChannelsInfo::getUserId).collect(Collectors.toSet()))
            );
            Map<Long, UserWithLogin> unknownPersonals = personals.getUnknownPersonals().stream().collect(Collectors.toMap(UserWithLogin::getUserId, Function.identity()));
            for (UserHostsChannelsInfo rawInfo : batch) {
                UserPersonalInfo personalInfo = personals.getResult().get(rawInfo.getUserId());
                if (personalInfo == null) {
                    if (personals.getUnknownUsers().contains(rawInfo.getUserId())) {
                        blackboxQ.put(rawInfo);
                    } else {
                        UserWithLogin userWithLogin = unknownPersonals.get(rawInfo.getUserId());
                        if (userWithLogin == null) {
                            break; // Вроде такого не должно быть
                        }
                        blackboxQ.put(new UserHostsChannelsInfo(rawInfo.getUserId(), rawInfo.getHost2Settings(), rawInfo.getEmail(), userWithLogin.getLogin()));
                    }
                } else {
                    out.put(new UserHostsPersonalsInfo(rawInfo, personalInfo));
                }
            }
        };
    }

    public BlockingBatchConsumer<UserHostsChannelsInfo> resolveBlackbox(GraphOutQueue<UserHostsPersonalsInfo> out) {
        return buffer -> {
            List<Long> unknownUsers = new ArrayList<>();
            List<UserWithLogin> unknownPersonals = new ArrayList<>();
            for (UserHostsChannelsInfo info : buffer) {
                if (info.getLogin() == null) {
                    unknownUsers.add(info.getUserId());
                } else {
                    unknownPersonals.add(new UserWithLogin(info.getUserId(), info.getLogin()));
                }
            }
            Map<Long, UserPersonalInfo> personals = RetryUtils.query(RETRY_POLICY,
                    () -> userPersonalInfoService.resolveCacheMisses(unknownUsers, unknownPersonals)
            );

            for (UserHostsChannelsInfo rawInfo : buffer) {
                UserPersonalInfo personalInfo = personals.get(rawInfo.getUserId());
                if (personalInfo != null) {
                    out.put(new UserHostsPersonalsInfo(rawInfo, personalInfo));
                }
            }
        };

    }

    @Required
    public void setUserPersonalInfoService(UserPersonalInfoService userPersonalInfoService) {
        this.userPersonalInfoService = userPersonalInfoService;
    }
}
