package com.atlassian.bitbucket.io;

import com.atlassian.utils.process.ProcessException;
import com.google.common.io.ByteStreams;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.io.*;

import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class TypeDetectingCopyOutputHandlerTest {

    @Mock
    private TypeAwareOutputSupplier supplier;
    @Mock
    private OutputStream outputStream;

    @Test
    public void testProcessTextFile() throws Exception {
        String path = "file.txt";
        byte[] fileContent = getFileContent(path);
        InputStream input = getClass().getResourceAsStream(path);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        when(supplier.getStream("text/plain")).thenReturn(outputStream);

        handler(path).process(input);
        assertArrayEquals(fileContent, outputStream.toByteArray());
    }

    @Test
    public void testProcessJpegFile() throws Exception {
        String path = "file.jpg";
        byte[] fileContent = getFileContent(path);
        InputStream input = getClass().getResourceAsStream(path);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        when(supplier.getStream("image/jpeg")).thenReturn(outputStream);

        handler(path).process(input);
        assertArrayEquals(fileContent, outputStream.toByteArray());
    }

    @Test
    public void testProcessJpegFileNoExtension() throws Exception {
        String path = "jpegFileWithoutExtension";
        byte[] fileContent = getFileContent(path);
        InputStream input = getClass().getResourceAsStream(path);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        when(supplier.getStream("image/jpeg")).thenReturn(outputStream);

        handler(path).process(input);
        assertArrayEquals(fileContent, outputStream.toByteArray());
    }

    @Test
    public void testProcessFileWithUnknownExtension() throws Exception {
        String path = "fileWithUnknownExtension.foo";
        byte[] fileContent = getFileContent(path);
        InputStream input = getClass().getResourceAsStream(path);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        when(supplier.getStream("text/plain")).thenReturn(outputStream);

        handler(path).process(input);
        assertArrayEquals(fileContent, outputStream.toByteArray());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testProcessInterrupted() throws Exception {
        String path = "file.txt";
        InputStream input = mock(InputStream.class);
        when(input.read(any(byte[].class), anyInt(), anyInt())).thenThrow(InterruptedIOException.class);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        when(supplier.getStream("image/jpeg")).thenReturn(outputStream);

        // When InterruptedIOException is thrown, it is voluntarily ignored, so no ProcessException should be thrown here.
        handler(path).process(input);
    }

    @SuppressWarnings("unchecked")
    @Test(expected = ProcessException.class)
    public void testProcessIOException() throws Exception {
        String path = "file.txt";
        InputStream input = mock(InputStream.class);
        when(input.read(any(byte[].class), anyInt(), anyInt())).thenThrow(IOException.class);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        when(supplier.getStream("image/jpeg")).thenReturn(outputStream);

        handler(path).process(input);
    }

    private byte[] getFileContent(String fileName) throws IOException {
        try (InputStream input = getClass().getResourceAsStream(fileName)) {
            return ByteStreams.toByteArray(input);
        }
    }

    private TypeDetectingCopyOutputHandler handler(String path) {
        return new TypeDetectingCopyOutputHandler(supplier, path);
    }
}
