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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.jetbrains.annotations.TestOnly;

import ru.yandex.qe.dispenser.domain.base_resources.BaseResourceLimit;

@TestOnly
public class BaseResourceLimitDaoImpl implements BaseResourceLimitDao {

    private final List<BaseResourceLimit> collection = new ArrayList<>();

    @Override
    public synchronized BaseResourceLimit create(BaseResourceLimit.Builder builder) {
        long nextId = collection.stream().max(Comparator.comparing(BaseResourceLimit::getId))
                .map(BaseResourceLimit::getId).orElse(-1L) + 1L;
        BaseResourceLimit result = builder.build(nextId);
        if (collection.stream().anyMatch(t -> t.getCampaignId() == result.getCampaignId()
                && t.getBaseResourceId() == result.getBaseResourceId())) {
            throw new IllegalArgumentException("Conflicting base resource limit already exists");
        }
        collection.add(result);
        return result;
    }

    @Override
    public synchronized Set<BaseResourceLimit> create(Collection<? extends BaseResourceLimit.Builder> builders) {
        if (builders.isEmpty()) {
            return Set.of();
        }
        long nextId = collection.stream().max(Comparator.comparing(BaseResourceLimit::getId))
                .map(BaseResourceLimit::getId).orElse(-1L) + 1L;
        Set<BaseResourceLimit> results = new HashSet<>();
        for (BaseResourceLimit.Builder builder : builders) {
            BaseResourceLimit result = builder.build(nextId);
            results.add(result);
            nextId++;
        }
        for (BaseResourceLimit result : results) {
            if (collection.stream().anyMatch(t -> t.getCampaignId() == result.getCampaignId()
                    && t.getBaseResourceId() == result.getBaseResourceId())) {
                throw new IllegalArgumentException("Conflicting base resource limit already exists");
            }
        }
        collection.addAll(results);
        return results;
    }

    @Override
    public synchronized Optional<BaseResourceLimit> getById(long id) {
        return collection.stream().filter(v -> v.getId() == id).findFirst();
    }

    @Override
    public synchronized Set<BaseResourceLimit> getByIds(Collection<? extends Long> ids) {
        if (ids.isEmpty()) {
            return Set.of();
        }
        return collection.stream().filter(v -> ids.contains(v.getId())).collect(Collectors.toSet());
    }

    @Override
    public synchronized List<BaseResourceLimit> getPage(Long idFrom, int size) {
        return Optional.ofNullable(idFrom).map(from -> collection.stream()
                        .sorted(Comparator.comparing(BaseResourceLimit::getId))
                .filter(v -> v.getId() > from).limit(size).collect(Collectors.toList()))
                .orElseGet(() -> collection.stream().sorted(Comparator.comparing(BaseResourceLimit::getId))
                        .limit(size).collect(Collectors.toList()));
    }

    @Override
    public synchronized Set<BaseResourceLimit> getByCampaign(long campaignId) {
        return collection.stream().filter(v -> v.getCampaignId() == campaignId).collect(Collectors.toSet());
    }

    @Override
    public synchronized Set<BaseResourceLimit> getByCampaigns(Collection<? extends Long> campaignIds) {
        if (campaignIds.isEmpty()) {
            return Set.of();
        }
        return collection.stream().filter(v -> campaignIds.contains(v.getCampaignId())).collect(Collectors.toSet());
    }

    @Override
    public synchronized Optional<BaseResourceLimit> update(BaseResourceLimit.Update update) {
        BaseResourceLimit updated = update.build();
        int index = -1;
        for (int i = 0; i < collection.size(); i++) {
            if (collection.get(i).getId() == updated.getId()) {
                index = i;
                break;
            }
        }
        if (index == -1) {
            return Optional.empty();
        }
        collection.remove(index);
        collection.add(updated);
        return Optional.of(updated);
    }

    @Override
    public synchronized Set<BaseResourceLimit> update(Collection<? extends BaseResourceLimit.Update> updates) {
        if (updates.isEmpty()) {
            return Set.of();
        }
        Set<BaseResourceLimit> result = new HashSet<>();
        for (BaseResourceLimit.Update update : updates) {
            BaseResourceLimit entity = update.build();
            int index = -1;
            for (int i = 0; i < collection.size(); i++) {
                if (collection.get(i).getId() == entity.getId()) {
                    index = i;
                    break;
                }
            }
            if (index == -1) {
                continue;
            }
            collection.remove(index);
            collection.add(entity);
            result.add(entity);
        }
        return result;
    }

    @Override
    public synchronized Optional<BaseResourceLimit> deleteById(long id) {
        int index = -1;
        for (int i = 0; i < collection.size(); i++) {
            if (collection.get(i).getId() == id) {
                index = i;
                break;
            }
        }
        if (index == -1) {
            return Optional.empty();
        }
        BaseResourceLimit removed = collection.get(index);
        collection.remove(index);
        return Optional.of(removed);
    }

    @Override
    public synchronized Set<BaseResourceLimit> deleteByIds(Collection<? extends Long> ids) {
        if (ids.isEmpty()) {
            return Set.of();
        }
        Set<BaseResourceLimit> result = new HashSet<>();
        for (long id : ids) {
            int index = -1;
            for (int i = 0; i < collection.size(); i++) {
                if (collection.get(i).getId() == id) {
                    index = i;
                    break;
                }
            }
            if (index == -1) {
                continue;
            }
            result.add(collection.get(index));
            collection.remove(index);
        }
        return result;
    }

    @Override
    public synchronized Optional<BaseResourceLimit> getByKey(BaseResourceLimit.Key key) {
        return collection.stream().filter(v -> v.toKey().equals(key)).findFirst();
    }

    @Override
    public synchronized Set<BaseResourceLimit> getByCampaignAndBaseResources(
            long campaignId, Collection<? extends Long> baseResourceIds) {
        if (baseResourceIds.isEmpty()) {
            return Set.of();
        }
        return collection.stream()
                .filter(v -> v.getCampaignId() == campaignId && baseResourceIds.contains(v.getBaseResourceId()))
                .collect(Collectors.toSet());
    }

    @Override
    public synchronized void clear() {
        collection.clear();
    }

}
