package ru.yandex.qe.dispenser.domain.dao.resource.unit;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.jetbrains.annotations.NotNull;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException;

import ru.yandex.qe.dispenser.domain.ResourceUnit;

public class ResourceUnitDaoImpl implements ResourceUnitDao {
    @NotNull
    protected final Map<Long, ResourceUnit> id2obj = new ConcurrentHashMap<>();

    @Override
    @NotNull
    public Set<ResourceUnit> getAll() {
        return new HashSet<>(id2obj.values());
    }

    @NotNull
    @Override
    public ResourceUnit create(@NotNull final ResourceUnit resourceUnit) {
        if (id2obj.put(resourceUnit.getResourceId(), resourceUnit) != null) {
            throw new DuplicateKeyException(resourceUnit.getClass().getSimpleName() + " object with id " + resourceUnit.getResourceId() + " already exists!");
        }
        return resourceUnit;
    }

    @NotNull
    @Override
    public ResourceUnit read(@NotNull final Long id) throws EmptyResultDataAccessException {
        return Optional.ofNullable(id2obj.get(id))
                .orElseThrow(() -> new EmptyResultDataAccessException("No object in " + getClass().getSimpleName() + " with id " + id, 1));
    }

    @NotNull
    @Override
    public Map<Long, ResourceUnit> read(@NotNull final Collection<Long> ids) {
        if (ids.isEmpty()) {
            return Collections.emptyMap();
        }
        return ids.stream()
                .filter(id2obj::containsKey)
                .distinct()
                .collect(Collectors.toMap(Function.identity(), id2obj::get));
    }

    @Override
    public boolean update(@NotNull final ResourceUnit resourceUnit) {
        if (id2obj.computeIfPresent(resourceUnit.getResourceId(), (pk, t) -> resourceUnit) == null) {
            throw new IllegalArgumentException(resourceUnit.getClass().getSimpleName() + " object not exists to update!");
        }
        return true;
    }

    @Override
    public boolean delete(@NotNull final ResourceUnit resourceUnit) {
        if (id2obj.remove(resourceUnit.getResourceId()) == null) {
            throw new DataSourceLookupFailureException(resourceUnit.getClass().getSimpleName() + " with id '" + resourceUnit.getResourceId() + "' not exists to delete!");
        }
        return true;
    }
}
