package com.atlassian.bitbucket.scm.cache.git;

import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import static org.junit.Assert.*;

public class PackRequestTest {

    private static final String WANTS =
            "006fwant 3d36b3a45440d4d6d26f941225ded5376dadbf29 multi_ack_detailed no-done side-band-64k thin-pack ofs-delta\n" +
            "0032want 4f4cfe40875a88985e048c31feb1fc9648f1966c\n" +
            "0032want 3e9f686ccf249d3199fcedee30989084625e106b\n";

    private static final String SHALLOWS =
            "0034shallow 0152153279aa76ad0ab4d8a8225de534e59ecd240034shallow 017bf15eb3c141dd568c37b02a282b6d91ecf34c" +
            "0034shallow 0412c352a40fdc52f28626870bb770049a6d0a910034shallow 04fe8536167d3b54a5234d9d3cfff91e750ff90c" +
            "0034shallow 0585f65c6f4dd95564df8da5fc834ee5c1eb5cef";

    private static final String DEEPEN = "000cdeepen 1";

    private static final String HAVES =
            "0032have 1b87ec4574cf048a161650228efaa6cd571b34a0\n" +
            "0032have 7faf0d05368111ac1b572a7556b1a4671cd83ed2\n" +
            "0032have 7f2a960915474d8f3e0ccac60d7f082ef5e8749a\n";

    private static final String DONE = "0009done\n";

    private static final String FLUSH_PACKET = "0000";

    @Test
    public void testClone() throws IOException {
        PackRequest packRequest = new PackRequest();
        process(packRequest, WANTS, FLUSH_PACKET, DONE);

        assertFalse(packRequest.isEmpty());
        assertTrue(packRequest.isComplete());
        assertFalse(packRequest.isFetch());
        assertFalse(packRequest.isShallow());
        assertEquals("clone", packRequest.getRequestType());
        assertNotNull(packRequest.buildCacheKey());
    }

    @Test
    public void testFetch() throws IOException {
        PackRequest packRequest = new PackRequest();
        process(packRequest, WANTS, FLUSH_PACKET, HAVES, FLUSH_PACKET, DONE);

        assertFalse(packRequest.isEmpty());
        assertTrue(packRequest.isComplete());
        assertTrue(packRequest.isFetch());
        assertFalse(packRequest.isShallow());
        assertEquals("fetch", packRequest.getRequestType());
        assertNull(packRequest.buildCacheKey());
    }

    @Test
    public void testFetchOnShallowClone() throws IOException {
        PackRequest packRequest = new PackRequest();
        process(packRequest, WANTS, SHALLOWS, FLUSH_PACKET);

        // at this point the server would send back shallow/unshallow lines
        assertFalse(packRequest.isComplete());
        assertTrue(packRequest.isFetch());
        assertFalse(packRequest.isShallow());
        assertEquals("fetch", packRequest.getRequestType());
        assertNull(packRequest.buildCacheKey());

        // afer the shallow/unshallows are processed, the client will send the haves
        process(packRequest, HAVES, FLUSH_PACKET, DONE);
        assertTrue(packRequest.isComplete());
        assertTrue(packRequest.isFetch());
        assertFalse(packRequest.isShallow());
        assertEquals("fetch", packRequest.getRequestType());
        assertNull(packRequest.buildCacheKey());
    }

    @Test(expected = IllegalArgumentException.class)
    public void testIllegalDeepen() throws IOException {
        process(new PackRequest(), WANTS, "000cdeepen #", FLUSH_PACKET, DONE);
    }

    @Test(expected = IllegalStateException.class)
    public void testIllegalContentAfterDone() throws IOException {
        process(new PackRequest(), WANTS, FLUSH_PACKET, DONE, DONE);
    }

    @Test(expected = IllegalStateException.class)
    public void testMissingFlushPacket() throws IOException {
        process(new PackRequest(), WANTS, DONE);
    }

    @Test
    public void testRefsUpToDate() throws IOException {
        PackRequest request = new PackRequest();
        process(request, FLUSH_PACKET);

        assertTrue(request.isEmpty());
        assertFalse(request.isShallow());
        assertFalse(request.isFetch());
        assertTrue(request.isComplete());
        assertEquals("refs", request.getRequestType());
        assertNull(request.buildCacheKey());
    }

    @Test
    public void testShallowClone() throws IOException {
        PackRequest packRequest = new PackRequest();
        process(packRequest, WANTS, DEEPEN, FLUSH_PACKET, DONE);

        assertFalse(packRequest.isEmpty());
        assertTrue(packRequest.isComplete());
        assertFalse(packRequest.isFetch());
        assertTrue(packRequest.isShallow());
        assertEquals("clone", packRequest.getRequestType());
        assertNotNull(packRequest.buildCacheKey());

    }

    @Test
    public void testShallowFetch() throws IOException {
        PackRequest packRequest = new PackRequest();
        process(packRequest, WANTS, DEEPEN, FLUSH_PACKET, HAVES, FLUSH_PACKET, DONE);

        assertTrue(packRequest.isComplete());
        assertTrue(packRequest.isFetch());
        assertTrue(packRequest.isShallow());
        assertEquals("fetch", packRequest.getRequestType());
        assertNull(packRequest.buildCacheKey());
    }

    @Test
    public void testShallowFetchOnShallowClone() throws IOException {
        PackRequest packRequest = new PackRequest();
        process(packRequest, WANTS, SHALLOWS, DEEPEN, FLUSH_PACKET);

        // at this point the server would send back shallow/unshallow lines
        assertFalse(packRequest.isComplete());
        assertTrue(packRequest.isFetch());
        assertTrue(packRequest.isShallow());
        // already known that it's a fetch because of the 'shallow' lines
        assertEquals("fetch", packRequest.getRequestType());
        assertNull(packRequest.buildCacheKey());

        // afer the shallow/unshallows are processed, the client will send the haves
        process(packRequest, HAVES, FLUSH_PACKET, DONE);
        assertTrue(packRequest.isComplete());
        assertTrue(packRequest.isFetch());
        assertTrue(packRequest.isShallow());
        assertEquals("fetch", packRequest.getRequestType());
        assertNull(packRequest.buildCacheKey());
    }

    private void process(PackRequest request, String...values) throws IOException {
        for (String value : values) {
            ByteArrayInputStream inputStream = new ByteArrayInputStream(value.getBytes());
            byte[] packet;
            while ((packet = GitUtils.readPacket(inputStream)) != null) {
                request.packet(packet);
            }
        }
    }
}
