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

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.inject.Inject;

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

import ru.yandex.qe.dispenser.domain.Person;
import ru.yandex.qe.dispenser.domain.PersonGroupMembership;
import ru.yandex.qe.dispenser.domain.PersonGroupMembershipType;
import ru.yandex.qe.dispenser.domain.Project;
import ru.yandex.qe.dispenser.domain.YaGroup;
import ru.yandex.qe.dispenser.domain.dao.InMemoryLongKeyDaoImpl;
import ru.yandex.qe.dispenser.domain.hierarchy.Role;
import ru.yandex.qe.dispenser.domain.index.LongIndexBase;

public class PersonGroupMembershipDaoImpl extends InMemoryLongKeyDaoImpl<PersonGroupMembership> implements PersonGroupMembershipDao {

    private final PersonProjectRelations userProjects;

    @Inject
    public PersonGroupMembershipDaoImpl(final PersonProjectRelations userProjects) {
        this.userProjects = userProjects;
    }

    @Override
    public void createIfAbsent(@NotNull final Collection<PersonGroupMembership> objects) {
        objects.forEach(this::createIfAbsent);
    }

    @NotNull
    @Override
    public List<PersonGroupMembership> findByPersons(@NotNull final Collection<Person> persons) {
        // Slow implementation, must never use in production
        final Set<Long> personIds = persons.stream().map(LongIndexBase::getId).collect(Collectors.toSet());
        return filter(m -> personIds.contains(m.getPerson().getId())).collect(Collectors.toList());
    }

    @NotNull
    @Override
    public Set<YaGroup> getAllPersonDepartmentsByPerson(@NotNull final Person person) {
        // Slow implementation, must never use in production
        return filter(m -> m.getPerson().getId() == person.getId()
                && (m.getMembershipType() == PersonGroupMembershipType.DEPARTMENT
                || m.getMembershipType() == PersonGroupMembershipType.DEPARTMENT_ANCESTORS))
                .map(PersonGroupMembership::getGroup).collect(Collectors.toSet());
    }

    @NotNull
    @Override
    public Set<Long> findPersonIdsByGroupId(final long groupId) {
        // Slow implementation, must never use in production
        return filter(m -> m.getGroup().getId() == groupId).map(m -> m.getPerson().getId()).collect(Collectors.toSet());
    }

    @NotNull
    @Override
    public Map<Long, Set<Long>> findPersonIdsByGroupIds(@NotNull final Set<Long> groupIds) {
        // Slow implementation, must never use in production
        final Map<Long, Set<Long>> result = new HashMap<>();
        groupIds.forEach(id -> result.put(id, new HashSet<>()));
        groupIds.forEach(groupId -> {
            result.put(groupId, filter(m -> m.getGroup().getId() == groupId).map(m -> m.getPerson().getId()).collect(Collectors.toSet()));
        });
        return result;
    }

    @NotNull
    @Override
    public Set<YaGroup> findGroupsByPersonId(final long personId) {
        // Slow implementation, must never use in production
        return filter(m -> m.getPerson().getId() == personId).map(PersonGroupMembership::getGroup).collect(Collectors.toSet());
    }

    @NotNull
    @Override
    public Map<Long, Set<YaGroup>> findGroupsByPersonIds(@NotNull final Set<Long> personIds) {
        // Slow implementation, must never use in production
        final Map<Long, Set<YaGroup>> result = new HashMap<>();
        personIds.forEach(id -> result.put(id, new HashSet<>()));
        personIds.forEach(personId -> {
            result.put(personId, filter(m -> m.getPerson().getId() == personId).map(PersonGroupMembership::getGroup).collect(Collectors.toSet()));
        });
        return result;
    }

    @NotNull
    @Override
    public Map<Long, Set<Long>> findPersonIdsByGroupIdsForProjectMembership() {
        // Slow implementation, must never use in production
        final Multimap<Project, YaGroup> responsibleGroupsByProject = userProjects.getLinkedPersonGroups(PersonProjectRelations.EntityType.GROUP, Role.RESPONSIBLE);
        final Multimap<Project, YaGroup> memberGroupsByProject = userProjects.getLinkedPersonGroups(PersonProjectRelations.EntityType.GROUP, Role.MEMBER);
        final HashSet<YaGroup> groups = new HashSet<>(responsibleGroupsByProject.values());
        groups.addAll(memberGroupsByProject.values());
        final Map<Long, Set<Long>> result = new HashMap<>();
        groups.forEach(group -> {
            result.put(group.getId(), filter(m -> m.getGroup().getId() == group.getId()).map(m -> m.getPerson().getId()).collect(Collectors.toSet()));
        });
        return result;
    }

}
