package com.atlassian.bitbucket.internal.mirroring.mirror.dao;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
* Stub for testing interactions with ProjectMappingDao
*/
public class StubProjectMappingDao implements ProjectMappingDao {

    private final Map<String, AoProjectMapping> dataByExternalId = new HashMap<>();
    private final Map<Integer, AoProjectMapping> dataByLocalId = new HashMap<>();

    public void add(AoProjectMapping mapping) {
        dataByExternalId.put(mappingKey(mapping.getUpstreamId(), mapping.getExternalId()), mapping);
        dataByLocalId.put(mapping.getLocalId(), mapping);
    }

    public void clearAllProgress(@Nonnull String upstreamServerId) {
        dataByLocalId.forEach((id, mapping) -> {
            if (mapping.getUpstreamId().equals(upstreamServerId)) {
                mapping.setInProgress(false);
            }
        });
    }

    @Nonnull
    @Override
    public AoProjectMapping create(@Nonnull Integer localId, @Nonnull String upstreamServerId, @Nonnull String upstreamProjectId) {
        AoProjectMapping mapping = mock(AoProjectMapping.class);
        when(mapping.getLocalId()).thenReturn(localId);
        when(mapping.getUpstreamId()).thenReturn(upstreamServerId);
        when(mapping.getExternalId()).thenReturn(upstreamProjectId);

        dataByExternalId.put(mappingKey(upstreamServerId, upstreamProjectId), mapping);
        dataByLocalId.put(localId, mapping);

        return mapping;
    }

    @Override
    public void delete(@Nonnull Integer localId) {
        AoProjectMapping deleted = dataByLocalId.remove(localId);
        if (deleted != null) {
            dataByExternalId.remove(mappingKey(deleted.getUpstreamId(), deleted.getExternalId()));
        }
    }

    @Override
    public void delete(@Nonnull String upstreamServerId, @Nonnull String upstreamProjectId) {
        AoProjectMapping deleted = dataByExternalId.remove(mappingKey(upstreamServerId, upstreamProjectId));
        if (deleted != null) {
            dataByLocalId.remove(deleted.getLocalId());
        }
    }

    @Override
    public AoProjectMapping getByLocalId(@Nonnull Integer localId) {
        return dataByLocalId.get(localId);
    }

    @Override
    public AoProjectMapping getByUpstreamId(@Nonnull String upstreamServerId, @Nonnull String upstreamProjectId) {
        return dataByExternalId.get(mappingKey(upstreamServerId, upstreamProjectId));
    }

    @Nonnull
    @Override
    public Iterable<AoProjectMapping> getByUpstreamId(@Nonnull String upstreamServerId, @Nonnull Iterable<String> upstreamProjectIds) {
        ArrayList<AoProjectMapping> mappings = new ArrayList<>();
        for (String upstreamProjectId : upstreamProjectIds) {
            mappings.add(dataByExternalId.get(mappingKey(upstreamServerId, upstreamProjectId)));
        }
        return mappings;
    }

    @Nonnull
    @Override
    public Set<Integer> getLocalIdsByUpstreamId(String upstreamId) {
        return dataByExternalId.values().stream().map(AoProjectMapping::getLocalId).collect(Collectors.toSet());
    }

    private static String mappingKey(Object upstreamId, Object externalId) {
        return upstreamId + "::" + externalId;
    }
}
