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

import com.atlassian.activeobjects.test.TestActiveObjects;
import com.atlassian.bitbucket.ao.AbstractAoDaoTest;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import net.java.ao.test.jdbc.Data;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.atlassian.bitbucket.internal.mirroring.dao.AoConstants.MAX_IN_CLAUSE_SIZE;
import static org.junit.Assert.*;

@Data(AoDatabaseUpdater.class)
public class AoProjectMappingDaoTest extends AbstractAoDaoTest {

    private static final String UPSTREAM_SERVER_ID = "upstream-server-id";
    private AoProjectMappingDao dao;

    public AoProjectMappingDaoTest() {
        super(AoProjectMapping.class);
    }

    @Before
    public void setUp() throws Exception {
        dao = new AoProjectMappingDao(new TestActiveObjects(entityManager));
    }

    @Test
    public void testCreate() {
        AoProjectMapping mapping = dao.create(99, "upstream-server", "aa-bb-cc-dd");

        assertMapping(99, "upstream-server", "aa-bb-cc-dd", mapping);
    }

    @Test
    public void testDelete() {
        AoProjectMapping mapping = dao.create(99, "upstream-server", "aa-bb-cc-dd");
        dao.delete(mapping.getUpstreamId(), mapping.getExternalId());

        assertNull(dao.getByUpstreamId("upstream-server", "aa-bb-cc-dd"));
    }

    @Test
    public void testGetByLocalId() {
        dao.create(101, UPSTREAM_SERVER_ID, "bb-cc-dd");

        AoProjectMapping retrieved = dao.getByLocalId(101);
        assertMapping(101, UPSTREAM_SERVER_ID, "bb-cc-dd", retrieved);
    }

    @Test
    public void testGetByLocalIdDoesNotExist() {
        assertNull(dao.getByLocalId(111111));
    }

    @Test
    public void testGetByUpstreamId() {
        dao.create(101, UPSTREAM_SERVER_ID, "bb-cc-dd");

        AoProjectMapping retrieved = dao.getByUpstreamId(UPSTREAM_SERVER_ID, "bb-cc-dd");
        assertMapping(101, UPSTREAM_SERVER_ID, "bb-cc-dd", retrieved);
    }

    @Test
    public void testGetByUpstreamIdDoesNotExist() {
        dao.create(101, UPSTREAM_SERVER_ID, "bb-cc-dd");

        assertNull(dao.getByUpstreamId("does-not-exist", "bb-cc-dd"));
        assertNull(dao.getByUpstreamId(UPSTREAM_SERVER_ID, "does-not-exist"));
        assertNull(dao.getByUpstreamId("does-not-exist", "nope"));
    }

    @Test
    public void testGetByUpstreamIdBatch() {
        dao.create(101, UPSTREAM_SERVER_ID, "aa-bb-cc");
        dao.create(102, UPSTREAM_SERVER_ID, "bb-cc-dd");
        dao.create(103, UPSTREAM_SERVER_ID, "cc-dd-ee");

        List<AoProjectMapping> mapping =
                Lists.newArrayList(dao.getByUpstreamId(UPSTREAM_SERVER_ID, Arrays.asList("aa-bb-cc", "cc-dd-ee")));
        assertEquals(2, mapping.size());
        assertMapping(101, UPSTREAM_SERVER_ID, "aa-bb-cc", mapping.get(0));
        assertMapping(103, UPSTREAM_SERVER_ID, "cc-dd-ee", mapping.get(1));
    }

    @Test
    public void testGetByUpstreamIdBatchCorrectServer() {
        dao.create(101, UPSTREAM_SERVER_ID, "aa-bb-cc");
        dao.create(102, "different-server", "bb-cc-dd");
        dao.create(103, UPSTREAM_SERVER_ID, "cc-dd-ee");

        List<AoProjectMapping> mapping =
                Lists.newArrayList(dao.getByUpstreamId(UPSTREAM_SERVER_ID, Arrays.asList("aa-bb-cc", "bb-cc-dd")));
        assertEquals(1, mapping.size());
        assertMapping(101, UPSTREAM_SERVER_ID, "aa-bb-cc", mapping.get(0));
    }

    @Test
    public void testGetByUpstreamIdLargeBatch() {
        ArrayList<String> externalIds = new ArrayList<>();
        for (int i = 0; i <= MAX_IN_CLAUSE_SIZE; i++) {
            String externalId = String.valueOf(i + 1);
            dao.create(i, UPSTREAM_SERVER_ID, externalId);
            externalIds.add(externalId);
        }

        List<AoProjectMapping> mapping =
                Lists.newArrayList(dao.getByUpstreamId(UPSTREAM_SERVER_ID, externalIds));
        assertEquals(101, mapping.size());
        assertMapping(0, UPSTREAM_SERVER_ID, "1", mapping.get(0));
        assertMapping(1, UPSTREAM_SERVER_ID, "2", mapping.get(1));
        assertMapping(99, UPSTREAM_SERVER_ID, "100", mapping.get(99));
        assertMapping(MAX_IN_CLAUSE_SIZE, UPSTREAM_SERVER_ID,
                String.valueOf(MAX_IN_CLAUSE_SIZE + 1), mapping.get(MAX_IN_CLAUSE_SIZE));
    }

    @Test
    public void testGetLocalIdsByUpstreamId() throws Exception {
        dao.create(101, UPSTREAM_SERVER_ID, "aa-bb-cc");
        dao.create(102, "different-server", "bb-cc-dd");
        dao.create(103, UPSTREAM_SERVER_ID, "cc-dd-ee");
        dao.create(104, "different-server", "dd-ee-ff");

        assertEquals(ImmutableSet.of(101, 103), dao.getLocalIdsByUpstreamId(UPSTREAM_SERVER_ID));
        assertEquals(ImmutableSet.of(102, 104), dao.getLocalIdsByUpstreamId("different-server"));
        assertTrue(dao.getLocalIdsByUpstreamId("no-such-server").isEmpty());
    }

    private void assertMapping(int localId, String upstreamId, String externalId, AoProjectMapping mapping) {
        assertNotNull(mapping);
        assertEquals(Integer.valueOf(localId), mapping.getLocalId());
        assertEquals(upstreamId, mapping.getUpstreamId());
        assertEquals(externalId, mapping.getExternalId());
    }}