package ru.yandex.qe.dispenser.domain.dao.person;

import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.google.common.collect.Multimap;
import org.jetbrains.annotations.NotNull;
import org.springframework.dao.EmptyResultDataAccessException;

import ru.yandex.qe.dispenser.domain.Person;
import ru.yandex.qe.dispenser.domain.Project;
import ru.yandex.qe.dispenser.domain.YaGroup;
import ru.yandex.qe.dispenser.domain.hierarchy.Role;
import ru.yandex.qe.dispenser.domain.index.LongIndexBase;
import ru.yandex.qe.dispenser.domain.util.CollectionUtils;

public class CachingPersonReader implements PersonReader {

    private final StaffCache staffCache;
    private final PersonProjectRelations relations;

    public CachingPersonReader(final StaffCache staffCache, final PersonProjectRelations relations) {
        this.staffCache = staffCache;
        this.relations = relations;
    }

    @NotNull
    @Override
    public Set<Project> getLinkedProjects(@NotNull final Person member, @NotNull final Role role) {
        final Set<Project> result = new HashSet<>(relations.getLinkedProjects(member, role));
        final Multimap<Project, YaGroup> project2groups = relations.getLinkedPersonGroups(PersonProjectRelations.EntityType.GROUP, role);
        final Set<YaGroup> personGroups = staffCache.getPersonGroups(member);
        final Set<Long> personGroupIds = personGroups.stream().filter(g -> !g.isDeleted()).map(LongIndexBase::getId).collect(Collectors.toSet());
        result.addAll(CollectionUtils.filter(project2groups, (p, g) -> personGroupIds.contains(g.getId())).keySet());
        return result;
    }

    @NotNull
    @Override
    public Optional<Person> tryReadPersonByUid(final long uid) {
        return staffCache.tryGetPersonByUid(uid);
    }

    @NotNull
    @Override
    public Set<Person> tryReadPersonsByUids(@NotNull final Collection<Long> uids) {
        return staffCache.tryGetPersonsByUids(uids);
    }

    @NotNull
    @Override
    public Optional<Person> tryReadPersonByLogin(@NotNull final String login) {
        return staffCache.tryGetPersonByLogin(login);
    }

    @NotNull
    @Override
    public Set<Person> tryReadPersonsByLogins(@NotNull final Collection<String> logins) {
        return staffCache.tryGetPersonsByLogins(logins);
    }

    @NotNull
    @Override
    public Optional<Person> tryReadPersonById(final long id) {
        return staffCache.tryGetPersonById(id);
    }

    @NotNull
    @Override
    public Set<Person> tryReadPersonsByIds(@NotNull final Collection<Long> ids) {
        return staffCache.tryGetPersonsByIds(ids);
    }

    @NotNull
    @Override
    public Person read(@NotNull final Long id) throws EmptyResultDataAccessException {
        return staffCache.tryGetPersonById(id).orElseThrow(() -> new EmptyResultDataAccessException("No person with id " + id, 1));
    }

    @NotNull
    @Override
    public Person read(@NotNull final String key) throws EmptyResultDataAccessException {
        return staffCache.tryGetPersonByLogin(key).orElseThrow(() -> new EmptyResultDataAccessException("No person with login " + key, 1));
    }

}
